2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

デザインパターンが速く書けるNemerleのマクロ

Posted at

概要

Nemerle Macro Libraryにある,デザインパターンを実装するために作られたマクロを紹介します.筆者のNemerleのバージョンは 1.1 on VisualStudio 2015です.


Design patterns ・ rsdn/nemerle Wikiの内容沿って作成しています.
一部,例を引用しているところがあります.これから紹介するマクロはこのソースコードによって実装されているので,不明なことがあれば,実装を見れば良いでしょう.

Abstract factoryパターン

.Netの標準ライブラリでこのデザインパターンを作ったものは見たことがありませんが, インスタンスの生成専用のクラスを用意して,整合性が必要な一連のクラスを近い場所に記述することによってコードのミスを減らすという狙いがあるデザインパターンです.詳しくはこの記事を読むとわかりやすいかもしれません.

class Factory 
{
  public virtual CreateWidget (name : string) : Widget {
    Widget (x)
  }
  public virtual CreateWorker (name : string, tasks : int) : Worker {
    Worker (name, tasks)
  }
}

class MyFactory : Factory 
{
  public override CreateWidget (name : string) : Widget {
    MyWidget (x)
  }
  public override CreateWorker (name : string, tasks : int) : Worker {
    MyWorker (name, tasks)
  }
}

これを,クラスにマクロをつけて簡単に書くことができます.

[Nemerle.DesignPatterns.AbstractFactory (Widget, Worker)]
class Factory {}

[Nemerle.DesignPatterns.AbstractFactory (Override (MyWidget, Widget), Override (MyWorker, Worker))]
class MyFactory : Factory {}

Composite(Aggregate)パターン

ディレクトリ構造を例に考えてみるとわかりやすいこのデザインパターンですが,このようなコードを,

[Nemerle.DesignPatterns.Aggregate (ReadUtil, WriteUtil)]
class ReadWriteUtil { 
  public this (inp : string, outp : string) {
    _initReadUtil (inp);   
    _initWriteUtil (outp);   
  }

  Run () : void {
    this.WriteFile (this.ReadFile ());
  }
}

こんな感じに展開してくれるそうです.

/*
 ReadFile()というメソッドを持つReadUtilとWriteFile(string)というメソッドを持つWriteUtilというクラスが別にある.
*/
class ReadWriteUtil {
   _ReadUtil : ReadUtil;
   _WriteUtil : WriteUtil;
   
   public this (inp : string, outp : string) {
       _initReadUtil(inp);
       _initWriteUtil(outp);
   }
   public _initReadUtil (x : string) : void { this._ReadUtil = new ReadUtil(x); }
   public _initWriteUtil (x : string) : void { this._WriteUtil = new WriteUtil(x); }

   public ReadFile () : string { this._ReadUtil.ReadFile(); }
   public WriteFile (x : string) : void { this._WriteUtil.WriteFile(x); }
}

Proxy パターン

生のオブジェクトのメソッドなどをラップするProxyクラス(代理人のクラス)を作るデザインパターンです.Wikipediaがわかりやすいかなと思います.以下に例を示します.

using System;
using System.Runtime.Remoting;

// "Subject"

public interface IMath
{
  // Methods
   Add( x :  double, y : double ) : double;
   Sub( x :  double, y : double ) : double;
   Mul( x :  double, y : double ) : double;
   Div( x :  double, y : double ) : double;
}

// "RealSubject"

class Math : MarshalByRefObject, IMath
{
  // Methods
  public Add(x : double, y : double ) : double { x + y; }
  public Sub(x : double, y : double ) : double { x - y; }
  public Mul(x : double, y : double ) : double { x * y; }
  public Div(x : double, y : double ) : double { x / y; }
}

// Remote "Proxy Object"
class MathProxy : IMath
{
  // the stubs implementing IMath by calling math.* are automatically generated
  // (that macro is not included in the standard library;
  // there is another proxy macro down the page)
  [DesignPatterns.Proxy (IMath)]
  math : Math;
  
  // Constructors
  public this()
  {
    // Create Math instance in a different AppDomain
    def ad = System.AppDomain.CreateDomain( "MathDomain",null, null );
    def o =
      ad.CreateInstance("Proxy_RealWorld", "Math", false,
                        System.Reflection.BindingFlags.CreateInstance, null, 
                        null, null,null,null );
    math = ( o.Unwrap() :> Math);
  }
}

public class ProxyApp
{
  public static Main() :  void
  {
    // Create math proxy
    def  p =  MathProxy();

    // Do the math
    Console.WriteLine( "4 + 2 = {0}", p.Add( 4.0, 2.0 ) );
    Console.WriteLine( "4 - 2 = {0}", p.Sub( 4.0, 2.0 ) );
    Console.WriteLine( "4 * 2 = {0}", p.Mul( 4.0, 2.0 ) );
    Console.WriteLine( "4 / 2 = {0}", p.Div( 4.0, 2.0 ) );
  }
}

個人的には,そんままラップしたりすることはないと思うので,あんまり使わないかなと思います.

シングルトンデザインパターン

日本語の説明サイトが多いシングルトンデザインパターンです.使う人も結構多いと思います.Nemerleで普通に書くと以下のようになります.

using Nemerle.Collections;
using System;
using System.Console;

module Program
{
     Main() : void
    {
        def serv1 = Server.Instance;
        def serv2 = Server.Instance;
        serv1.AddMessage("Hoge");
        WriteLine(serv2.LatestMessage);
    }
}

class Server {
    private static _Instance : Server = Server();

    private mutable Messages : list[string] = [];
    protected this() {}
    
    public static Instance : Server {
        get {
            _Instance
        }
    } 
    
    public LatestMessage : string {
        get {
            Messages.Head;   
        }
    }
    public AddMessage(s : string) : void {
        Messages = s :: Messages;
    }
}

出力は同じインスタンスのServer.Messagesを参照しているわけなので,serv1で追加された"Hoge"が出力されます.DesignPatterns.Singletonマクロを使うと少し短く書けます.

using Nemerle.Collections;
using System;
using System.Console;

module Program
{
     Main() : void
    {
        def serv1 = Server.Instance;
        def serv2 = Server.Instance;
        serv1.AddMessage("Hoge");
        WriteLine(serv2.LatestMessage);
    }
}

[Nemerle.DesignPatterns.Singleton()]
class Server {
    private mutable Messages : list[string] = [];
    
    protected this() {} // privateにした方がいいって警告が出る.
    
    public LatestMessage : string {
        get {
            Messages.Head;   
        }
    }
    
    public AddMessage(s : string) : void {
        Messages = s :: Messages;
    }
}

Githubドキュメントだと,Instanceに当たるメンバの名前をマクロの引数で変えられるよと書いてありましたが,最新版ではInstanceという名前から変えられないようです.

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?