概要
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
という名前から変えられないようです.