読者です 読者をやめる 読者になる 読者になる

謎言語使いの徒然

適当に気になった技術や言語を流すブログ。

AOP(Seaser.NET)をC#でやってみる

.NET C# OSS

前回の続きで、今度はAOP(Aspect-Oriented Programming。アスペクト指向プログラミング)を試してみる。
とりあえず、dll コピーと、参照設定は前回のものと同様にしておく。

AOP アスペクト指向プログラミング

どんなもんかといいますと、以下Wikipediaから
>>オブジェクト指向ではうまく分離できない特徴(クラス間を横断 (cross-cutting) するような機能)を「アスペクト」とみなし、アスペクト記述言語をもちいて分離して記述することでプログラムに柔軟性をもたせようとする試み。<<
なんだそうです。
ハイ、意味不明。そんなものよりコードで理解するほうがきっと早い。
今回、元になるコードは以下の通り。

namespace AOPSample
{
    class Sample
    {
        private string _prefix;

        public Sample(string prefix)
        {
            _prefix = prefix;
        }

        public string GetHelloMessage(string name)
        {
            string prefName = _prefix + "," + name;
            return "Hello " + prefName;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Sample sample = new Sample("Mr");
            Console.WriteLine(sample.GetHelloMessage("Azalea"));
        }
    }
}

これにロギング機能を付けてみたいと思う。
通常、メソッドのログをとるなら、以下の形になると思う。

public string GetHelloMessage(string name)
{
    Console.WriteLine("GetHelloMessage is called.");
    string prefName = _prefix + "," + name;
    Console.WriteLine("return GetHelloMessage.");
    return "Hello " + prefName;
}

しかし、これは「ログをとる」というロジックと「文字列を作成する」というロジックを分離できてないとも言える。
ということで、その分離を行えるのがAOPということで、実際にやってみる。
先ずは、既存の変更箇所を設定する。

class Sample : MarshalByRefObject
{
    /* 略 */
}

MarshalByRefObject を継承しただけ。これは、ドメイン間通信できるオブジェクトであることを証明するもの(逆に言えば、ソレができなければダメってことらしい)
Interface を用意して、それを経由するなら、特に必要ないらしい(要確認)
そして、実際にログを吐き出すコードを記述する。

public class SampleInspector
    : Seasar.Framework.Aop.Interceptors.AbstractInterceptor
{
    public override object Invoke
        (Seasar.Framework.Aop.IMethodInvocation invocation)
    {
        string methodName = invocation.Method.DeclaringType.Name
            + "." + invocation.Method.Name;

        Console.WriteLine("開始 " + methodName);

        object ret = invocation.Proceed();

        Console.WriteLine("終了 " + methodName);
        return ret;
    }
}

invocation にメソッド実行可能状態でデータが入ってくると。
後は割りと見たままですね。
そして、その関連付けを外部ファイルで設定します。

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container//EN" "http://www.seasar.org/dtd/components.dtd">
<components>
  <component name="SampleInspector"
      class="AOPContainerTest.SampleInspector" />

  <component class="AOPContainerTest.Sample">
    <arg>"Mr"</arg>
    <aspect pointcut="GetHelloMessage">SampleInspector</aspect>
  </component>
</components>

AOPContainerTest.Sample コンポーネントを登録しておいて、どのメソッドにどの Inspector を関連付けるか指定する。
後は呼び出し側を前回同様に書き換える。

static void Main(string[] args)
{
    Seasar.Framework.Container.IS2Container container
        = Seasar.Framework.Container.Factory.S2ContainerFactory.Create("AspectSample.dicon");

    Sample sample = (Sample)container.GetComponent(typeof(Sample));
    Console.WriteLine(sample.GetHelloMessage("Azalea"));
}

これで、ログ出力の切り離しができた。
ログのとりかたを変更するにも、Inspector の指定を変えるだけだわっ。
こういう動的性は便利だね。