2006年04月26日

Interpreter ― 文法規則をクラスで表現する

   このエントリーをはてなブックマークに追加 Clip to Evernote
ここに掲載したコードは、『増補改訂版Java言語で学ぶデザインパターン入門 / 結城 浩(著)』に掲載されているサンプルコードをC#に移植したものです。移植後のコードの公開に関しては、結城氏の了解を得ています。
※当ソースは、Visual C# 2005 Express Editionで動作を確認しています。



ParseExceptionインターフェース
using System;

namespace Gushwell.DesignPatterns {
[Serializable]
public class ParseException : System.Exception {
public ParseException(string msg)
: base(msg) {
}
}
}

Nodeクラス
using System;

namespace Gushwell.DesignPatterns {
public abstract class Node {
public abstract void Parse(Context context);
}
}



ProgramNodeクラス
using System;

namespace Gushwell.DesignPatterns {
// <program> ::= program <command list>
public class ProgramNode : Node {
private Node commandListNode;

public override void Parse(Context context) {
context.SkipToken("program");
commandListNode = new CommandListNode();
commandListNode.Parse(context);
}

public override string ToString() {
return "[program " + commandListNode + "]";
}
}
}



CommandListNodeクラス
using System;
using System.Collections.Generic;

namespace Gushwell.DesignPatterns {
// <command list> ::= <command>* end
public class CommandListNode : Node {
private List<Node> list = new List<Node>();
public override void Parse(Context context) {
while ( true ) {
if ( context.CurrentToken() == null ) {
throw new ParseException("Missing 'end'");
} else if ( context.CurrentToken().Equals("end") ) {
context.SkipToken("end");
break;
} else {
Node commandNode = new CommandNode();
commandNode.Parse(context);
list.Add(commandNode);
}
}
}

public override string ToString() {
string s = "[";
for ( int i = 0; i < list.Count; i++ ) {
Node node = list[i];
s += node.ToString() + ( ( i == list.Count - 1 ) ? "" : "," );
}
return s + "]";
}
}
}



CommandNodeクラス
using System;

namespace Gushwell.DesignPatterns {
// <command> ::= <repeat command> | <primitive command>
public class CommandNode : Node {
private Node node;

public override void Parse(Context context) {
if ( context.CurrentToken().Equals("repeat") ) {
node = new RepeatCommandNode();
node.Parse(context);
} else {
node = new PrimitiveCommandNode();
node.Parse(context);
}
}

public override string ToString() {
return node.ToString();
}
}
}



<b>RepeatCommandNodeクラス</b>  
using System;

namespace Gushwell.DesignPatterns {
// <repeat command> ::= repeat <number> <command list>
public class RepeatCommandNode : Node {
private int number;
private Node commandListNode;

public override void Parse(Context context) {
context.SkipToken("repeat");
number = context.CurrentNumber();
context.NextToken();
commandListNode = new CommandListNode();
commandListNode.Parse(context);
}

public override string ToString() {
return "[repeat " + number + " " + commandListNode.ToString() + "]";
}
}
}



PrimitiveCommandNodeクラス
using System;

namespace Gushwell.DesignPatterns {
// <primitive command> ::= go | right | left
public class PrimitiveCommandNode : Node {
private string name;

public override void Parse(Context context) {
name = context.CurrentToken();
context.SkipToken(name);
if ( !name.Equals("go") && !name.Equals("right") &&
!name.Equals("left") ) {
throw new ParseException(name + " is undefined");
}
}

public override string ToString() {
return name;
}
}
}



StringTokenizerクラス
using System;
using System.Collections.Generic;
using System.Text;

namespace Gushwell.DesignPatterns {
class StringTokenizer {
private string[] strs;
private int index = -1;

public StringTokenizer(string text) {
this.strs = text.Split();
}
public string NextToken() {
return strs[++index];
}

public string CurrentToken() {
return strs[index];
}

public bool HasMoreTokens() {
return ( index + 1 ) < strs.Length;
}
}
}



Contextクラス
using System;

namespace Gushwell.DesignPatterns {
public class Context {
private StringTokenizer tokenizer;
private string currentToken;

public Context(string text) {
tokenizer = new StringTokenizer(text);
NextToken();
}

public virtual string NextToken() {
if ( tokenizer.HasMoreTokens() ) {
currentToken = tokenizer.NextToken();
} else {
currentToken = null;
}
return currentToken;
}

public virtual string CurrentToken() {
return currentToken;
}

public virtual void SkipToken(string token) {
if ( !token.Equals(currentToken) ) {
throw new ParseException("Warning: " + token +
" is expected, but " + currentToken + " is found.");
}
NextToken();
}

public virtual int CurrentNumber() {
int number = 0;
try {
number = System.Int32.Parse(currentToken);
} catch ( System.FormatException e ) {
throw new ParseException("Warning: " + e);
}
return number;
}
}
}



Programクラス
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace Gushwell.DesignPatterns {
class Program {
static void Main(string[] args) {
try {
StreamReader reader =
new StreamReader("program.txt", System.Text.Encoding.Default);
System.String text;
while ( ( text = reader.ReadLine() ) != null ) {
Console.WriteLine("text = \"" + text + "\"");
Node node = new ProgramNode();
node.Parse(new Context(text));
Console.WriteLine("node = " + node);

}
Console.ReadLine();
} catch ( System.Exception ex ) {
Console.Error.WriteLine(ex.StackTrace);
}
}
}
}

  

Posted by gushwell at 21:31Comments(0)TrackBack(0)

2006年04月19日

Command ― 命令をクラスにする

   このエントリーをはてなブックマークに追加 Clip to Evernote
ここに掲載したコードは、『増補改訂版Java言語で学ぶデザインパターン入門 / 結城 浩(著)』に掲載されているサンプルコードをC#に移植したものです。移植後のコードの公開に関しては、結城氏の了解を得ています。
※当ソースは、Visual C# 2005 Express Editionで動作を確認しています。



ICommandインターフェース
using System;

namespace Gushwell.DesignPatterns.Command {

public interface ICommand {
void Execute();
}
}


DrawCommandクラス
using System;
using System.Drawing;
using Gushwell.DesignPatterns.Command;

namespace Gushwell.DesignPatterns.Drawer {

public class DrawCommand : ICommand {
// 描画対象
protected internal IDrawable drawable;

// 描画位置
private System.Drawing.Point position;

// コンストラクタ
public DrawCommand(IDrawable drawable, Point position) {
this.drawable = drawable;
this.position = position;
}

// 実行
public virtual void Execute() {
drawable.Draw(position.X, position.Y);
}
}
}


MacroCommandクラス
using System;
using System.Collections;

namespace Gushwell.DesignPatterns.Command {

public class MacroCommand : ICommand {
// 命令の集合
private Stack commands = new Stack();

// 実行
public virtual void Execute() {
foreach (ICommand cmd in commands) {
cmd.Execute();
}
}

// 追加
public virtual void Append(ICommand cmd) {
if (cmd != this) {
commands.Push(cmd);
}
}

// 最後の命令を削除
public virtual void Undo() {
if (!(commands.Count == 0)) {
commands.Pop();
}
}

// 全部削除
public virtual void Clear() {
commands.Clear();
}
}
}



IDrawableインターフェース
using System;

namespace Gushwell.DesignPatterns.Drawer {

public interface IDrawable {
void Draw(int x, int y);
}
}



DrawCanvasクラス
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using Gushwell.DesignPatterns.Command;

namespace Gushwell.DesignPatterns.Drawer {
public partial class DrawCanvas : UserControl, IDrawable {
public DrawCanvas() {
InitializeComponent();
}
// 描画色
private Color color = Color.Red;
// 描画する点の半径
private int radius = 6;
// 履歴
private MacroCommand history = null;

public void Draw(int x, int y) {
using ( Graphics g = CreateGraphics() )
using ( Brush brush = new SolidBrush(color) ) {
g.FillEllipse(brush, x - radius, y - radius, radius * 2, radius * 2);
}
}
public void SetHistory(MacroCommand history) {
this.history = history;
}

private void DrawCanvas_Paint(object sender, PaintEventArgs e) {
if (history != null)
history.Execute();
}
}
}



DrawCanvasクラス (IDEが作成したソース : DrawCanvas.Designer.cs)
namespace Gushwell.DesignPatterns.Drawer {
partial class DrawCanvas {
private System.ComponentModel.IContainer components = null;

protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}

#region コンポーネント デザイナで生成されたコード

/// <summary>
/// デザイナ サポートに必要なメソッドです。このメソッドの内容を
/// コード エディタで変更しないでください。
/// </summary>
private void InitializeComponent() {
this.SuspendLayout();
//
// DrawCanvas
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Name = "DrawCanvas";
this.Paint +=
new System.Windows.Forms.PaintEventHandler(this.DrawCanvas_Paint);
this.ResumeLayout(false);

}

#endregion
}
}



Form1クラス
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Gushwell.DesignPatterns.Command;
using Gushwell.DesignPatterns.Drawer;

namespace Gushwell.DesignPatterns {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
drawCanvas1.SetHistory(history);
}

private void Form1_MouseDown(object sender, MouseEventArgs e) {
//userControl11.draw(e.X, e.Y);
}
private MacroCommand history = new MacroCommand();

private bool drag = false;
private void drawCanvas1_MouseDown(object sender, MouseEventArgs e) {
drag = true;

}

private void drawCanvas1_MouseMove(object sender, MouseEventArgs e) {
if (drag) {
ICommand cmd = new DrawCommand(drawCanvas1, e.Location);
history.Append(cmd);
cmd.Execute();
}
}

private void drawCanvas1_MouseUp(object sender, MouseEventArgs e) {
drag = false;
}

private void clearButton_Click(object sender, EventArgs e) {
history.Clear();
drawCanvas1.Invalidate();
}
}
}



Form1クラス (IDEが作成したソース : Form1.Designer.cs)
using Gushwell.DesignPatterns.Drawer;

namespace Gushwell.DesignPatterns {
partial class Form1 {
private System.ComponentModel.IContainer components = null;

protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}

#region Windows フォーム デザイナで生成されたコード

private void InitializeComponent() {
this.clearButton = new System.Windows.Forms.Button();
this.drawCanvas1 = new Gushwell.DesignPatterns.Drawer.DrawCanvas();
this.SuspendLayout();
//
// clearButton
//
this.clearButton.Location = new System.Drawing.Point(104, 3);
this.clearButton.Name = "clearButton";
this.clearButton.Size = new System.Drawing.Size(75, 23);
this.clearButton.TabIndex = 1;
this.clearButton.Text = "Clear";
this.clearButton.UseVisualStyleBackColor = true;
this.clearButton.Click += new System.EventHandler(this.clearButton_Click);
//
// drawCanvas1
//
this.drawCanvas1.BackColor = System.Drawing.SystemColors.Window;
this.drawCanvas1.Location = new System.Drawing.Point(1, 32);
this.drawCanvas1.Name = "drawCanvas1";
this.drawCanvas1.Size = new System.Drawing.Size(290, 239);
this.drawCanvas1.TabIndex = 0;
this.drawCanvas1.MouseDown +=
new System.Windows.Forms.MouseEventHandler(this.drawCanvas1_MouseDown);
this.drawCanvas1.MouseMove +=
new System.Windows.Forms.MouseEventHandler(this.drawCanvas1_MouseMove);
this.drawCanvas1.MouseUp +=
new System.Windows.Forms.MouseEventHandler(this.drawCanvas1_MouseUp);
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(292, 273);
this.Controls.Add(this.clearButton);
this.Controls.Add(this.drawCanvas1);
this.Name = "Form1";
this.Text = "Command Pattern Sample";
this.MouseDown +=
new System.Windows.Forms.MouseEventHandler(this.Form1_MouseDown);
this.ResumeLayout(false);

}

#endregion

private DrawCanvas drawCanvas1;
private System.Windows.Forms.Button clearButton;
}
}


Programクラス
using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace Gushwell.DesignPatterns {
static class Program {
/// <summary>
/// アプリケーションのメイン エントリ ポイントです。
/// </summary>
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}

  
Posted by gushwell at 21:58Comments(0)TrackBack(0)

2006年04月14日

Proxy ― 必要になってから作る

   このエントリーをはてなブックマークに追加 Clip to Evernote
ここに掲載したコードは、『増補改訂版Java言語で学ぶデザインパターン入門 / 結城 浩(著)』に掲載されているサンプルコードをC#に移植したものです。移植後のコードの公開に関しては、結城氏の了解を得ています。
※当ソースは、Visual C# 2005 Express Editionで動作を確認しています。



IPrintableインターフェース
using System;

namespace Gushwell.DesignPatterns {
public interface IPrintable {
void SetPrinterName(string name); // 名前の設定
string GetPrinterName(); // 名前の取得
void Print(string string_Renamed); // 文字列表示(プリントアウト)
}
}



Printerクラス
using System;

namespace Gushwell.DesignPatterns {
public class Printer : IPrintable {
private string name;

// コンストラクタ
public Printer() {
HeavyJob("Printerのインスタンスを生成中");
}

// コンストラクタ
public Printer(string name) {
this.name = name;
HeavyJob("Printerのインスタンス(" + name + ")を生成中");
}

// 名前の設定
public virtual void SetPrinterName(string name) {
this.name = name;
}

// 名前の取得
public virtual string GetPrinterName() {
return name;
}

// 名前付きで表示
public virtual void Print(string string_Renamed) {
Console.WriteLine("=== " + name + " ===");
Console.WriteLine(string_Renamed);
}

// 重い作業(のつもり)
private void HeavyJob(string msg) {
Console.Write(msg);
for (int i = 0; i < 5; i++) {
System.Threading.Thread.Sleep(1000);
Console.Write(".");
}
Console.WriteLine("完了。");
}
}
}



PrinterProxyクラス
using System;

namespace Gushwell.DesignPatterns {
public class PrinterProxy : IPrintable {
private string name; // 名前
private Printer real; // 「本人」

public PrinterProxy() {
}

// コンストラクタ
public PrinterProxy(string name) {
this.name = name;
}

// 名前の設定
public virtual void SetPrinterName(string name) {
lock (this) {
if (real != null) {
real.SetPrinterName(name); // 「本人」にも設定する
}
this.name = name;
}
}

// 名前の取得
public virtual string GetPrinterName() {
return name;
}

// 表示
public virtual void Print(string string_Renamed) {
Realize();
real.Print(string_Renamed);
}

// 「本人」を生成
private void Realize() {
lock (this) {
if (real == null) {
real = new Printer(name);
}
}
}
}
}



Programクラス
using System;

namespace Gushwell.DesignPatterns {
public class Program {

[STAThread]
public static void Main(string[] args) {
IPrintable p = new PrinterProxy("Alice");
Console.WriteLine("名前は現在" + p.GetPrinterName() + "です。");
p.SetPrinterName("Bob");
Console.WriteLine("名前は現在" + p.GetPrinterName() + "です。");
p.Print("Hello, world.");
}
}
}

  
Posted by gushwell at 20:17Comments(0)TrackBack(0)

2006年04月04日

Flyweight ― 同じものを共有して無駄をなくす

   このエントリーをはてなブックマークに追加 Clip to Evernote
ここに掲載したコードは、『増補改訂版Java言語で学ぶデザインパターン入門 / 結城 浩(著)』に掲載されているサンプルコードをC#に移植したものです。移植後のコードの公開に関しては、結城氏の了解を得ています。
※当ソースは、Visual C# 2005 Express Editionで動作を確認しています。



BigCharクラス
using System;
using System.IO;
using System.Text;

namespace Gushwell.DesignPatterns {
public class BigChar {
private char charname; // 文字の名前
private string fontdata; // 大きな文字を表現する文字列('#' '.' '\n'の列)

// コンストラクタ
public BigChar(char charname) {
this.charname = charname;
try {
StreamReader reader = new StreamReader("big" + charname + ".txt");
string line;
StringBuilder buf = new StringBuilder();
while ( ( line = reader.ReadLine() ) != null ) {
buf.Append(line);
buf.Append("\n");
}
reader.Close();
this.fontdata = buf.ToString();
} catch ( System.IO.IOException ) {
this.fontdata = charname + "?";
}
}

// 大きな文字を表示する
public virtual void Print() {
Console.Write(fontdata);
}
}
}


BigCharFactoryクラス
using System;
using System.Collections.Generic;

namespace Gushwell.DesignPatterns {
public sealed class BigCharFactory {
// すでに作ったBigCharのインスタンスを管理
private Dictionary<char, BigChar> pool = new Dictionary<char, BigChar>();

// Singletonパターン
private static readonly BigCharFactory singleton = new BigCharFactory();

// 唯一のインスタンスを得る
public static BigCharFactory Instance {
get { return singleton; }
}

// コンストラクタ
private BigCharFactory() {
}

// BigCharのインスタンス生成(共有)
public BigChar GetBigChar(char charname) {
lock ( this ) {
BigChar bc = null;
if ( pool.ContainsKey(charname) ) {
bc = pool[charname];
} else {
bc = new BigChar(charname); // ここでBigCharのインスタンスを生成
pool[charname] = bc;
}
return bc;
}
}
}
}


BigStringクラス
using System;

namespace Gushwell.DesignPatterns {
public class BigString {
private BigChar[] bigchars; // 「大きな文字」の配列

// コンストラクタ
public BigString(string str) {
bigchars = new BigChar[str.Length];
BigCharFactory factory = BigCharFactory.Instance;
for ( int i = 0; i < bigchars.Length; i++ ) {
bigchars[i] = factory.GetBigChar(str[i]);
}
}

// 表示
public virtual void Print() {
foreach ( BigChar bc in bigchars ) {
bc.Print();
}
}
}
}


Programクラス
using System;

namespace Gushwell.DesignPatterns {
public class Program {
public static void Main(string[] args) {
if ( args.Length == 0 ) {
Console.WriteLine("Usage: FlyWeightSample digits");
Console.WriteLine("Example: FlyWeightSample 1212123");
return;
}
BigString bs = new BigString(args[0]);
bs.Print();
}
}
}

  
Posted by gushwell at 23:26Comments(0)TrackBack(0)

2006年03月29日

State ― 状態をクラスとして表現する

   このエントリーをはてなブックマークに追加 Clip to Evernote
ここに掲載したコードは、『増補改訂版Java言語で学ぶデザインパターン入門 / 結城 浩(著)』に掲載されているサンプルコードをC#に移植したものです。移植後のコードの公開に関しては、結城氏の了解を得ています。
※当ソースは、Visual C# 2005 Express Editionで動作を確認しています。



IContextインターフェース
using System;

namespace Gushwell.DesignPatterns {
public interface IContext {
void SetClock(int hour); // 時刻の設定
void ChangeState(State state); // 状態変化
void CallSecurityCenter(string msg); // 警備センター警備員呼び出し
void RecordLog(string msg); // 警備センター記録
}
}


Stateインターフェース
using System;

namespace Gushwell.DesignPatterns {
public interface State {
void DoClock(IContext context, int hour); // 時刻設定
void DoUse(IContext context); // 金庫使用
void DoAlarm(IContext context); // 非常ベル
void DoPhone(IContext context); // 通常通話
}
}


DayStateクラス
using System;

namespace Gushwell.DesignPatterns {
public sealed class DayState : State {
// 唯一のインスタンスを得る
public static State Instance {
get { return singleton; }
}
private static readonly DayState singleton = new DayState();

// コンストラクタはprivate
private DayState() {
}

// 時刻設定
public void DoClock(IContext context, int hour) {
if (hour < 9 || 17 <= hour) {
context.ChangeState(NightState.Instance);
}
}

// 金庫使用
public void DoUse(IContext context) {
context.RecordLog("金庫使用(昼間)");
}

// 非常ベル
public void DoAlarm(IContext context) {
context.CallSecurityCenter("非常ベル(昼間)");
}

// 通常通話
public void DoPhone(IContext context) {
context.CallSecurityCenter("通常の通話(昼間)");
}

// 文字列表現
public override string ToString() {
return "[昼間]";
}
}
}


NightStateクラス
using System;

namespace Gushwell.DesignPatterns {
public sealed class NightState : State {
// 唯一のインスタンスを得る
public static State Instance {
get { return singleton; }
}
private static readonly NightState singleton = new NightState();

// コンストラクタはprivate
private NightState() {
}

// 時刻設定
public void DoClock(IContext context, int hour) {
if (9 <= hour && hour < 17) {
context.ChangeState(DayState.Instance);
}
}

// 金庫使用
public void DoUse(IContext context) {
context.CallSecurityCenter("非常:夜間の金庫使用!");
}

// 非常ベル
public void DoAlarm(IContext context) {
context.CallSecurityCenter("非常ベル(夜間)");
}

// 通常通話
public void DoPhone(IContext context) {
context.RecordLog("夜間の通話録音");
}

// 文字列表現
public override string ToString() {
return "[夜間]";
}
}
}


SafeFormクラス
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace Gushwell.DesignPatterns {
public partial class SafeForm : Form, IContext {
private State state = DayState.Instance;
public SafeForm() {
InitializeComponent();
}

private void ActionPerformed(object sender, EventArgs e) {
Console.WriteLine(e.ToString());
if (sender == buttonUse) {
// 金庫使用ボタン
state.DoUse(this);
} else if (sender == buttonAlarm) {
// 非常ベルボタン
state.DoAlarm(this);
} else if (sender == buttonPhone) {
// 通常通話ボタン
state.DoPhone(this);
} else if (sender == buttonExit) {
// 終了ボタン
System.Environment.Exit(0);
} else {
Console.WriteLine("?");
}
}

// 時刻の設定
virtual public void SetClock(int hour) {
string clockstring = "現在時刻は";
if (hour < 10) {
clockstring += ("0" + hour + ":00");
} else {
clockstring += (hour + ":00");
}
Console.WriteLine(clockstring);
textClock.Text = clockstring;
state.DoClock(this, hour);
}

public virtual void ChangeState(State state) {
Console.WriteLine(this.state + "から" + state
+ "へ状態が変化しました。");
this.state = state;
}

// 警備センター警備員呼び出し
public virtual void CallSecurityCenter(string msg) {
textScreen.AppendText("call! " + msg + "\n");
}

// 警備センター記録
public virtual void RecordLog(string msg) {
textScreen.AppendText("record ... " + msg + "\n");
}

private void Form1_Load(object sender, EventArgs e) {
timerClock.Interval = 1000;
timerClock.Enabled = true;
}

private int hour = 0;
private void timer1_Tick(object sender, EventArgs e) {
SetClock(hour++);
if (hour >= 24)
hour = 0;
}

}
}


Programクラス
using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace Gushwell.DesignPatterns {
static class Program {
/// <summary>
/// アプリケーションのメイン エントリ ポイントです。
/// </summary>
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new SafeForm());
}
}
}


SafeFormクラス (IDEが作成したソース : SafeForm.Designer.cs)
namespace Gushwell.DesignPatterns {
partial class SafeForm {
private System.ComponentModel.IContainer components = null;

protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}

#region Windows フォーム デザイナで生成されたコード

private void InitializeComponent() {
this.components = new System.ComponentModel.Container();
this.textScreen = new System.Windows.Forms.TextBox();
this.textClock = new System.Windows.Forms.TextBox();
this.buttonUse = new System.Windows.Forms.Button();
this.buttonAlarm = new System.Windows.Forms.Button();
this.buttonPhone = new System.Windows.Forms.Button();
this.buttonExit = new System.Windows.Forms.Button();
this.timerClock = new System.Windows.Forms.Timer(this.components);
this.SuspendLayout();
//
// textScreen
//
this.textScreen.Anchor = ((System.Windows.Forms.AnchorStyles)
((((System.Windows.Forms.AnchorStyles.Top
| System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.textScreen.Location = new System.Drawing.Point(0, 28);
this.textScreen.Multiline = true;
this.textScreen.Name = "textScreen";
this.textScreen.ReadOnly = true;
this.textScreen.Size = new System.Drawing.Size(348, 175);
this.textScreen.TabIndex = 0;
//
// textClock
//
this.textClock.Anchor = ((System.Windows.Forms.AnchorStyles)
(((System.Windows.Forms.AnchorStyles.Top
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.textClock.Location = new System.Drawing.Point(0, 3);
this.textClock.Name = "textClock";
this.textClock.ReadOnly = true;
this.textClock.Size = new System.Drawing.Size(348, 19);
this.textClock.TabIndex = 1;
//
// buttonUse
//
this.buttonUse.Anchor = ((System.Windows.Forms.AnchorStyles)
((System.Windows.Forms.AnchorStyles.Bottom
| System.Windows.Forms.AnchorStyles.Left)));
this.buttonUse.Location = new System.Drawing.Point(6, 215);
this.buttonUse.Name = "buttonUse";
this.buttonUse.Size = new System.Drawing.Size(75, 23);
this.buttonUse.TabIndex = 2;
this.buttonUse.Text = "金庫使用";
this.buttonUse.UseVisualStyleBackColor = true;
this.buttonUse.Click += new System.EventHandler(this.ActionPerformed);
//
// buttonAlarm
//
this.buttonAlarm.Anchor = ((System.Windows.Forms.AnchorStyles)
((System.Windows.Forms.AnchorStyles.Bottom
| System.Windows.Forms.AnchorStyles.Left)));
this.buttonAlarm.Location = new System.Drawing.Point(91, 215);
this.buttonAlarm.Name = "buttonAlarm";
this.buttonAlarm.Size = new System.Drawing.Size(75, 23);
this.buttonAlarm.TabIndex = 3;
this.buttonAlarm.Text = "非常ベル";
this.buttonAlarm.UseVisualStyleBackColor = true;
this.buttonAlarm.Click += new System.EventHandler(this.ActionPerformed);
//
// buttonPhone
//
this.buttonPhone.Anchor = ((System.Windows.Forms.AnchorStyles)
((System.Windows.Forms.AnchorStyles.Bottom
| System.Windows.Forms.AnchorStyles.Left)));
this.buttonPhone.Location = new System.Drawing.Point(176, 215);
this.buttonPhone.Name = "buttonPhone";
this.buttonPhone.Size = new System.Drawing.Size(75, 23);
this.buttonPhone.TabIndex = 4;
this.buttonPhone.Text = "通常通話";
this.buttonPhone.UseVisualStyleBackColor = true;
this.buttonPhone.Click += new System.EventHandler(this.ActionPerformed);
//
// buttonExit
//
this.buttonExit.Anchor = ((System.Windows.Forms.AnchorStyles)
((System.Windows.Forms.AnchorStyles.Bottom
| System.Windows.Forms.AnchorStyles.Left)));
this.buttonExit.Location = new System.Drawing.Point(261, 215);
this.buttonExit.Name = "buttonExit";
this.buttonExit.Size = new System.Drawing.Size(75, 23);
this.buttonExit.TabIndex = 5;
this.buttonExit.Text = "終了";
this.buttonExit.UseVisualStyleBackColor = true;
this.buttonExit.Click += new System.EventHandler(this.ActionPerformed);
//
// timerClock
//
this.timerClock.Tick += new System.EventHandler(this.timer1_Tick);
//
// SafeForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(348, 248);
this.Controls.Add(this.buttonExit);
this.Controls.Add(this.buttonPhone);
this.Controls.Add(this.buttonAlarm);
this.Controls.Add(this.buttonUse);
this.Controls.Add(this.textClock);
this.Controls.Add(this.textScreen);
this.Name = "SafeForm";
this.Text = "Form1";
this.Load += new System.EventHandler(this.Form1_Load);
this.ResumeLayout(false);
this.PerformLayout();

}

#endregion

private System.Windows.Forms.TextBox textScreen;
private System.Windows.Forms.TextBox textClock;
private System.Windows.Forms.Button buttonUse;
private System.Windows.Forms.Button buttonAlarm;
private System.Windows.Forms.Button buttonPhone;
private System.Windows.Forms.Button buttonExit;
private System.Windows.Forms.Timer timerClock;
}
}

  
Posted by gushwell at 22:55Comments(0)TrackBack(0)

2006年03月23日

Memento ― 状態を保存する

   このエントリーをはてなブックマークに追加 Clip to Evernote
ここに掲載したコードは、『増補改訂版Java言語で学ぶデザインパターン入門 / 結城 浩(著)』に掲載されているサンプルコードをC#に移植したものです。移植後のコードの公開に関しては、結城氏の了解を得ています。
※当ソースは、Visual C# 2005 Express Editionで動作を確認しています。



Mementoクラス
using System;
using System.Collections.Generic;

namespace Gushwell.DesignPatterns.Game {

public class Memento {
private int money; // 所持金
private List<string> fruits; // フルーツ

// コンストラクタ(wide interface)
public Memento(int money) {
this.money = money;
this.fruits = new List<string>();
}

// 所持金を得る(narrow interface)
public virtual int Money {
get { return money; }

}

// フルーツを得る(wide interface)
public virtual List<string> Fruits {
get {
List<string> clone = new List<string>();
foreach (string s in fruits) {
clone.Add(s);
}
return clone;
}
}

// フルーツを追加する(wide interface)
public virtual void AddFruit(string fruit) {
fruits.Add(fruit);
}
}
}


Gamerクラス
using System;
using System.Collections.Generic;

namespace Gushwell.DesignPatterns.Game {

public class Gamer {
private int money; // 所持金
private List<string> fruits = new List<string>(); // フルーツ
private Random random = new Random(); // 乱数発生器
private static string[] fruitsname =
new string[] { "リンゴ", "ぶどう", "バナナ", "みかん" };

// コンストラクタ
public Gamer(int money) {
this.money = money;
}

// 現在の所持金を得る
public virtual int Money {
get { return money; }
}

// フルーツを1個得る
private string GetFruit() {
string prefix = "";
if (random.Next(0, 2) == 0) {
prefix = "おいしい";
}
return prefix + fruitsname[random.Next(fruitsname.Length)];
}

public virtual void Bet() {
// 賭ける…ゲームの進行
int dice = random.Next(6) + 1; // サイコロを振る
if (dice == 1) {
// 1の目…所持金が増える
money += 100;
Console.WriteLine("所持金が増えました。");
} else if (dice == 2) {
// 2の目…所持金が半分になる
money /= 2;
Console.WriteLine("所持金が半分になりました。");
} else if (dice == 6) {
// 6の目…フルーツをもらう
string f = GetFruit();
Console.WriteLine("フルーツ(" + f + ")をもらいました。");
fruits.Add(f);
} else {
// それ以外…何も起きない
Console.WriteLine("何も起こりませんでした。");
}
}

// スナップショットをとる
public virtual Memento CreateMemento() {
Memento m = new Memento(money);
foreach (string f in fruits) {
if (f.StartsWith("おいしい")) {
// フルーツはおいしいものだけ保存
m.AddFruit(f);
}
}
return m;
}

// アンドゥを行う
public virtual void RestoreMemento(Memento memento) {
this.money = memento.Money;
this.fruits = memento.Fruits;
}

// 文字列表現
public override string ToString() {
string s = string.Join(", ", fruits.ToArray());

return string.Format("[money = {0}, fruits = [{1}]]", money, s);
}
}
}


Programクラス
using System;
using System.Threading;
using Gushwell.DesignPatterns.Game;
using System.Collections.Generic;

namespace Gushwell.DesignPatterns {

public class Program {

public static void Main(string[] args) {
Gamer gamer = new Gamer(100); // 最初の所持金は100
Memento memento = gamer.CreateMemento(); // 最初の状態を保存しておく

for (int i = 0; i < 100; i++) {
Console.WriteLine("==== " + i); // 回数表示
Console.WriteLine("現状:" + gamer); // 現在の主人公の状態表示

gamer.Bet(); // ゲームを進める

Console.WriteLine("所持金は" + gamer.Money + "円になりました。");

// Mementoの取り扱いの決定
if (gamer.Money > memento.Money) {
Console.WriteLine(" (だいぶ増えたので、現在の状態を保存しておこう)");
memento = gamer.CreateMemento();
} else if (gamer.Money < memento.Money / 2) {
Console.WriteLine(" (だいぶ減ったので、以前の状態に復帰しよう)");
gamer.RestoreMemento(memento);
}

// 時間待ち
Thread.Sleep(1000);
Console.WriteLine("");
}
}
}
}

  
Posted by gushwell at 23:10Comments(0)TrackBack(0)

2006年03月22日

Observer −状態の変化を通知する

   このエントリーをはてなブックマークに追加 Clip to Evernote
ここに掲載したコードは、『増補改訂版Java言語で学ぶデザインパターン入門 / 結城 浩(著)』に掲載されているサンプルコードをC#に移植したものです。移植後のコードの公開に関しては、結城氏の了解を得ています。
※当ソースは、Visual C# 2005 Express Editionで動作を確認しています。



IObserverインターフェース
using System;

namespace Gushwell.DesignPatterns {
public interface IObserver {
void Update(NumberGenerator generator);
}
}


NumberGeneratorクラス
using System;
using System.Collections;

namespace Gushwell.DesignPatterns {

public abstract class NumberGenerator {
public abstract int Number { get;}
// Observerたちを保持
private ArrayList observers = new ArrayList();

// Observerを追加
public virtual void AddObserver(IObserver observer) {
observers.Add( observer );
}

// Observerを削除
public virtual void DeleteObserver(IObserver observer) {
observers.Remove( observer );
}

// Observerへ通知
public virtual void NotifyObservers() {
foreach ( IObserver o in observers ) {
o.Update( this );
}
}

// 数を生成する
public abstract void Execute();
}
}


RandomNumberGeneratorクラス
using System;

namespace Gushwell.DesignPatterns {
public class RandomNumberGenerator : NumberGenerator {
private Random random = new Random(); // 乱数生成機
private int number; // 現在の数

// 数を取得する
public override int Number {
get { return number; }
}

public override void Execute() {
for ( int i = 0; i < 20; i++ ) {
number = random.Next( 50 );
NotifyObservers();
}
}
}
}


DigitObserverクラス
using System;
using System.Threading;

namespace Gushwell.DesignPatterns {

public class DigitObserver : IObserver {
public virtual void Update(NumberGenerator generator) {
Console.WriteLine( "DigitObserver:" + generator.Number );
Thread.Sleep( 100 );
}
}
}


GraphObserverクラス
using System;
using System.Threading;

namespace Gushwell.DesignPatterns {

public class GraphObserver : IObserver {
public virtual void Update(NumberGenerator generator) {
Console.Write( "GraphObserver:" );
int count = generator.Number;
for ( int i = 0; i < count; i++ ) {
Console.Write( "*" );
}
Console.WriteLine( "" );
Thread.Sleep( 100 );
}
}
}


Programクラス
using System;

namespace Gushwell.DesignPatterns {

public class Program {
public static void Main(string[] args) {
NumberGenerator generator = new RandomNumberGenerator();
IObserver observer1 = new DigitObserver();
IObserver observer2 = new GraphObserver();
generator.AddObserver( observer1 );
generator.AddObserver( observer2 );
generator.Execute();
}
}
}

  
Posted by gushwell at 07:05Comments(0)TrackBack(0)

2006年03月20日

Mediator −相手は相談役1人だけ

   このエントリーをはてなブックマークに追加 Clip to Evernote
ここに掲載したコードは、『増補改訂版Java言語で学ぶデザインパターン入門 / 結城 浩(著)』に掲載されているサンプルコードをC#に移植したものです。移植後のコードの公開に関しては、結城氏の了解を得ています。
※当ソースは、Visual C# 2005 Express Editionで動作を確認しています。



IColleagueインターフェース
using System;

namespace Gushwell.DesignPatterns {

public interface IColleague {
void SetMediator(IMediator mediator);
void SetColleagueEnabled(bool enabled);
}
}


ColleagueButtonクラス
using System;

namespace Gushwell.DesignPatterns {

[Serializable]
public class ColleagueButton : System.Windows.Forms.Button, IColleague {
private IMediator mediator;
public ColleagueButton()
: base() {
}
// Mediatorを保持
virtual public void SetMediator(IMediator value) {
this.mediator = value;
}

// Mediatorから有効/無効が指示される
virtual public void SetColleagueEnabled(bool value) {
Enabled = value;
}
}
}


ColleagueRadioButtonクラス
using System;

namespace Gushwell.DesignPatterns {

[Serializable]
public class ColleagueRadioButton : System.Windows.Forms.RadioButton, IColleague {
private IMediator mediator = null;
public ColleagueRadioButton()
: base() {
}

// Mediatorを保持
virtual public void SetMediator(IMediator value) {
this.mediator = value;
}

// Mediatorから有効/無効が指示される
virtual public void SetColleagueEnabled(bool value) {
Enabled = value;
}

protected override void OnCheckedChanged(EventArgs e) {
base.OnCheckedChanged(e);
if (mediator != null)
mediator.ColleagueChanged();
}
}
}


ColleagueTextFieldクラス
using System;

namespace Gushwell.DesignPatterns {

[Serializable]
public class ColleagueTextField : System.Windows.Forms.TextBox, IColleague {
private IMediator mediator = null;
public ColleagueTextField()
: base() {
}

// Mediatorを保持
virtual public void SetMediator(IMediator value) {
this.mediator = value;
}

// Mediatorから有効/無効が指示される
virtual public void SetColleagueEnabled(bool value) {
Enabled = value;
}

// 文字列が変化したらMediatorに通知
protected override void OnTextChanged(EventArgs e) {
base.OnTextChanged(e);
if (mediator != null)
mediator.ColleagueChanged();
}
}
}


IMediatorインターフェース
using System;

namespace Gushwell.DesignPatterns {

public interface IMediator {
void CreateColleagues();
void ColleagueChanged();
}
}


LoginFormクラス
using System;
using System.Windows.Forms;

namespace Gushwell.DesignPatterns {
public partial class LoginForm : Form, IMediator {
public LoginForm() {
InitializeComponent();
CreateColleagues();
ColleagueChanged();
}

// 名前が実態と一致しなくなったが、オリジナルの名前のままとする。
public virtual void CreateColleagues() {
checkGuest.SetMediator(this);
checkLogin.SetMediator(this);
textUser.SetMediator(this);
textPass.SetMediator(this);
buttonOk.SetMediator(this);
buttonCancel.SetMediator(this);
}

// Colleageからの通知で各Colleageの有効/無効を判定する。
public virtual void ColleagueChanged() {
if (checkGuest.Checked) {
// Guest mode
textUser.SetColleagueEnabled(false);
textPass.SetColleagueEnabled(false);
buttonOk.SetColleagueEnabled(true);
} else {
// Login mode
textUser.SetColleagueEnabled(true);
UserpassChanged();
}
}
// textUserまたはtextPassの変更があった。
// 各Colleageの有効/無効を判定する。
private void UserpassChanged() {
if (textUser.Text.Length > 0) {
textPass.SetColleagueEnabled(true);
if (textPass.Text.Length > 0) {
buttonOk.SetColleagueEnabled(true);
} else {
buttonOk.SetColleagueEnabled(false);
}
} else {
textPass.SetColleagueEnabled(false);
buttonOk.SetColleagueEnabled(false);
}
}

private void buttonOk_Click(object sender, EventArgs e) {
Console.WriteLine(sender + " : " + e.ToString());
Close();
}
}
}


LoginFormクラス (IDEが作成したソース : LoginForm.Designer.cs)
namespace Gushwell.DesignPatterns {
partial class LoginForm {
private System.ComponentModel.IContainer components = null;

protected override void Dispose(bool disposing) {
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}

#region Windows フォーム デザイナで生成されたコード

private void InitializeComponent() {
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.textPass = new ColleagueTextField();
this.textUser = new ColleagueTextField();
this.buttonCancel = new ColleagueButton();
this.buttonOk = new ColleagueButton();
this.checkLogin = new ColleagueRadioButton();
this.checkGuest = new ColleagueRadioButton();
this.SuspendLayout();
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(11, 48);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(58, 12);
this.label1.TabIndex = 2;
this.label1.Text = "Username:";
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(13, 82);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(56, 12);
this.label2.TabIndex = 3;
this.label2.Text = "Password:";
//
// textPass
//
this.textPass.Location = new System.Drawing.Point(76, 74);
this.textPass.MaxLength = 10;
this.textPass.Name = "textPass";
this.textPass.PasswordChar = '*';
this.textPass.Size = new System.Drawing.Size(118, 19);
this.textPass.TabIndex = 7;
//
// textUser
//
this.textUser.Location = new System.Drawing.Point(75, 41);
this.textUser.MaxLength = 10;
this.textUser.Name = "textUser";
this.textUser.Size = new System.Drawing.Size(119, 19);
this.textUser.TabIndex = 6;
//
// buttonCancel
//
this.buttonCancel.Location = new System.Drawing.Point(110, 117);
this.buttonCancel.Name = "buttonCancel";
this.buttonCancel.Size = new System.Drawing.Size(93, 23);
this.buttonCancel.TabIndex = 5;
this.buttonCancel.Text = "Cancel";
this.buttonCancel.UseVisualStyleBackColor = true;
this.buttonCancel.Click += new System.EventHandler(this.buttonOk_Click);
//
// buttonOk
//
this.buttonOk.Location = new System.Drawing.Point(3, 117);
this.buttonOk.Name = "buttonOk";
this.buttonOk.Size = new System.Drawing.Size(101, 23);
this.buttonOk.TabIndex = 4;
this.buttonOk.Text = "OK";
this.buttonOk.UseVisualStyleBackColor = true;
this.buttonOk.Click += new System.EventHandler(this.buttonOk_Click);
//
// checkLogin
//
this.checkLogin.AutoSize = true;
this.checkLogin.Location = new System.Drawing.Point(87, 12);
this.checkLogin.Name = "checkLogin";
this.checkLogin.Size = new System.Drawing.Size(50, 16);
this.checkLogin.TabIndex = 1;
this.checkLogin.TabStop = true;
this.checkLogin.Text = "Login";
this.checkLogin.UseVisualStyleBackColor = true;
//
// checkGuest
//
this.checkGuest.AutoSize = true;
this.checkGuest.Location = new System.Drawing.Point(16, 12);
this.checkGuest.Name = "checkGuest";
this.checkGuest.Size = new System.Drawing.Size(53, 16);
this.checkGuest.TabIndex = 0;
this.checkGuest.TabStop = true;
this.checkGuest.Text = "Guest";
this.checkGuest.UseVisualStyleBackColor = true;
//
// LoginForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(206, 146);
this.Controls.Add(this.textPass);
this.Controls.Add(this.textUser);
this.Controls.Add(this.buttonCancel);
this.Controls.Add(this.buttonOk);
this.Controls.Add(this.label2);
this.Controls.Add(this.label1);
this.Controls.Add(this.checkLogin);
this.Controls.Add(this.checkGuest);
this.Name = "LoginForm";
this.Text = "Mediator Sample";
this.ResumeLayout(false);
this.PerformLayout();

}

#endregion

private ColleagueRadioButton checkGuest;
private ColleagueRadioButton checkLogin;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private ColleagueButton buttonOk;
private ColleagueButton buttonCancel;
private ColleagueTextField textUser;
private ColleagueTextField textPass;

}
}


Programクラス
using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace Gushwell.DesignPatterns {
static class Program {
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new LoginForm());
}
}
}


注意点:
ColleagueButton、ColleagueRadioButton、ColleagueTextField の3つのクラスをプロジェクトに追加するとツールボックスにこれら3つのコントロールが追加されますので、このコントロールを使って、以下の画面を参考に、フォームをデザインしてください。
  
Posted by gushwell at 07:00Comments(0)TrackBack(0)