使用地道的 C# 语言来表现设计模式
注:本文的目的不是谈论设计模式的实现,而是侧重讲如何把C#语法实际运用到设计模式中去。我想C#5 C#6时代再来看这些设计,恐怕又觉得笨拙了。所以,我想通过这篇文章,给大家一些更好地使用C#语言的启示,而不是给出设计模式实现的固定套路。限于水平有限,本文只能是抛砖引玉,欢迎楼下改进、补充。
注意,一切没有意义的发言,比如顶、支持、学习、mark、收藏、看看各类灌水等请不要留,以便大家更好地参与交流讨论。
首先展示的是迭代器模式和装饰模式:
遍历TreeView是很多人问的问题,用它来展示迭代器模式非常合适,事实上,下面的代码本身就可以拿来用:
我们给TreeView增加一个GetAllNodes()的方法:
使用:
在这个代码中,使用了C#的yield return,它在内部构造了一个状态机,从而实现了枚举接口。
内部,使用了递归来遍历结点。
同时我们用扩展方法来封装这个操作。为原本不支持遍历TreeNode的TreeView增加了功能,这就是装饰模式。
然后我们展示下代理模式。
这个例子是从 http://topic.csdn.net/u/20110415/16/112401aa-2fd7-4f96-8f99-ed33d7739e91.html 受到的启发。
很明显,C#没有办法向某些动态语言那样获得参数,那么怎么做呢?
看代码:
在这里,所有到 foo 的调用请求被转发到了 _foo 上面,而 _foo 因为使用了参数数组,所以可以按照参数传递的顺序获得参数。
模板方法:
模板方法在事件处理中被大量使用,下面给出一个简化的模型:
下面是策略模式
这个我们讨论过了,在这里,我们使用了两种策略,一个是把信息输出到控制台,一个是把信息输出到 output window。
还是这个例子,下面来演示简单工厂和单例:
我们把Log的生成交给一个工厂来做,而工厂只需要一个即可。这里使用了静态类的语法,实现了单例。
关于桥模式和享元模式,这里不提供例子,但是举实际的例子来说明问题。
桥模式的例子参考 http://topic.csdn.net/u/20110414/10/06526263-9e0d-491f-b963-7ae0afd1f7e4.html
这就是一个典型的桥模式的应用。作为客户端代码我们围绕LINQ编程而和数据库交互的实现是EF框架做的EF框架桥接了数据库查询和对象查询的具体实现。
下面的例子综合了策略模式、享元模式、工厂方法:
我们使用C#3的扩展方法给员工增加了工资计算的方法。
在内部,我们使用享元模式,从而为员工附加上对应的工资数据。
在使用扩展方法的时候,需要强调一点:目前只有C#支持扩展方法,所以其它语言,必须使用传统的方法调用,这就使得this对象可能为null,所以一定要先判断!
我们按照.NET的习惯,如果对象为null,丢出NullReferenceException。
另外,扩展方法只作用于定义的那个命名空间。这也为我们扩展不同的工资算法提供了可能。
因为扩展方法中保存了对象扩展的数据(这里的_data),所以对于持久化/序列化的时候要注意状态的保持和恢复。建议配合重写GetHashCode()实现这一点。
最后说下。如果扩展方法和类自身的成员重名,而且方法参数相同,执行哪一个呢?记住,类的成员方法优先级更高。
最后,我们用到了动态对象。
注意,一切没有意义的发言,比如顶、支持、学习、mark、收藏、看看各类灌水等请不要留,以便大家更好地参与交流讨论。
首先展示的是迭代器模式和装饰模式:
遍历TreeView是很多人问的问题,用它来展示迭代器模式非常合适,事实上,下面的代码本身就可以拿来用:
我们给TreeView增加一个GetAllNodes()的方法:
static class TreeViewNodeIterator
{
private static IEnumerable<TreeNode> GetChild(TreeNode tnparent)
{
foreach (TreeNode tn in tnparent.Nodes)
{
if (tn.Nodes.Count > 0)
{
foreach (TreeNode tnchild in GetChild(tn))
{
yield return tnchild;
}
}
yield return tn;
}
}
public static IEnumerable<TreeNode> GetAllNodes(this TreeView treeview)
{
if (treeview == null) throw new NullReferenceException();
foreach (TreeNode tn in treeview.Nodes)
{
if (tn.Nodes.Count > 0)
{
foreach (TreeNode tnchild in GetChild(tn))
{
yield return tnchild;
}
}
yield return tn;
}
}
}使用:
private void button1_Click(object sender, EventArgs e)
{
foreach (TreeNode tn in treeView1.GetAllNodes())
Debug.WriteLine(tn.Text);
}在这个代码中,使用了C#的yield return,它在内部构造了一个状态机,从而实现了枚举接口。
内部,使用了递归来遍历结点。
同时我们用扩展方法来封装这个操作。为原本不支持遍历TreeNode的TreeView增加了功能,这就是装饰模式。
然后我们展示下代理模式。
这个例子是从 http://topic.csdn.net/u/20110415/16/112401aa-2fd7-4f96-8f99-ed33d7739e91.html 受到的启发。
很明显,C#没有办法向某些动态语言那样获得参数,那么怎么做呢?
看代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
Test.foo(3, 5);
}
}
static class Test
{
private static void _foo(params int[] args)
{
for (int i = 0; i < args.GetLength(0); i++)
{
Console.WriteLine(args[i]);
}
}
public static void foo(int x, int y)
{
_foo(x, y);
}
}
}
在这里,所有到 foo 的调用请求被转发到了 _foo 上面,而 _foo 因为使用了参数数组,所以可以按照参数传递的顺序获得参数。
模板方法:
模板方法在事件处理中被大量使用,下面给出一个简化的模型:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
MyWindow win = new MyWindow();
win.Close();
}
}
class MyWindow : Window
{
protected sealed override void OnBeforeClose(out bool Cancel)
{
Console.WriteLine("Are you sure to close this window? (yes/no)");
string s = Console.ReadLine();
if (s.ToLower() != "yes")
Cancel = true;
else
Cancel = false;
}
protected sealed override void OnClosing()
{
Console.WriteLine("Please wait while the window is closing...");
}
}
class Window
{
protected virtual void OnBeforeClose(out bool Cancel) { Cancel = false; }
protected virtual void OnClosing() { }
protected virtual void OnClosed() { }
public void Close()
{
bool Cancel = false;
OnBeforeClose(out Cancel);
if (Cancel != true)
{
OnClosing();
OnClosed();
Console.WriteLine("Close() Finished!");
}
else
{
Console.WriteLine("Close() Canceled!");
}
}
}
}
下面是策略模式
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
namespace ConsoleApplication2
{
class Program
{
static private Action<string> DebugWriter = s => Debug.WriteLine(s);
static private Action<string> ConsoleWriter = s => Console.WriteLine(s);
static void Main(string[] args)
{
Log log1 = new Log(DebugWriter);
log1.WriteLog("I am writing to outpot window!");
Log log2 = new Log(ConsoleWriter);
log2.WriteLog("I am writing to console!");
}
}
class Log
{
private Action<string> _writer = s => Console.WriteLine(s);
public Log(Action<string> writer)
{
if (writer != null) _writer = writer;
}
public void WriteLog(string s)
{
_writer(s);
}
}
}
这个我们讨论过了,在这里,我们使用了两种策略,一个是把信息输出到控制台,一个是把信息输出到 output window。
还是这个例子,下面来演示简单工厂和单例:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
Log log1 = Factory.GetLog(Factory.LogType.Debug);
log1.WriteLog("I am writing to outpot window!");
Log log2 = Factory.GetLog(Factory.LogType.Console);
log2.WriteLog("I am writing to console!");
}
}
static class Factory
{
public enum LogType
{
Debug,
Console
};
static private Action<string> DebugWriter = s => Debug.WriteLine(s);
static private Action<string> ConsoleWriter = s => Console.WriteLine(s);
public static Log GetLog(LogType Type)
{
if (Type == LogType.Console) return new Log(ConsoleWriter);
if (Type == LogType.Debug) return new Log(DebugWriter);
return new Log(null);
}
}
class Log
{
private Action<string> _writer = s => Console.WriteLine(s);
public Log(Action<string> writer)
{
if (writer != null) _writer = writer;
}
public void WriteLog(string s)
{
_writer(s);
}
}
}
我们把Log的生成交给一个工厂来做,而工厂只需要一个即可。这里使用了静态类的语法,实现了单例。
关于桥模式和享元模式,这里不提供例子,但是举实际的例子来说明问题。
桥模式的例子参考 http://topic.csdn.net/u/20110414/10/06526263-9e0d-491f-b963-7ae0afd1f7e4.html
这就是一个典型的桥模式的应用。作为客户端代码我们围绕LINQ编程而和数据库交互的实现是EF框架做的EF框架桥接了数据库查询和对象查询的具体实现。
下面的例子综合了策略模式、享元模式、工厂方法:
我们使用C#3的扩展方法给员工增加了工资计算的方法。
在内部,我们使用享元模式,从而为员工附加上对应的工资数据。
在使用扩展方法的时候,需要强调一点:目前只有C#支持扩展方法,所以其它语言,必须使用传统的方法调用,这就使得this对象可能为null,所以一定要先判断!
我们按照.NET的习惯,如果对象为null,丢出NullReferenceException。
另外,扩展方法只作用于定义的那个命名空间。这也为我们扩展不同的工资算法提供了可能。
因为扩展方法中保存了对象扩展的数据(这里的_data),所以对于持久化/序列化的时候要注意状态的保持和恢复。建议配合重写GetHashCode()实现这一点。
最后说下。如果扩展方法和类自身的成员重名,而且方法参数相同,执行哪一个呢?记住,类的成员方法优先级更高。
最后,我们用到了动态对象。
...全文
相关推荐
DesignPattern:C#设计模式示例
DesignPattern:C#设计模式示例
DesignPattern新版設計模式(C#)
DesignPattern 新版設計模式(C#) 講述23種設計模式: ◆結構圖 ◆生活例子 ◆意圖 ◆適用性 ◆示例代碼 ◆實際應用
DesignPattern.NET:C#用户的设计模式集合
DesignPattern.NET C#用户的设计模式集合 概括 最近,我们专注于为开源项目做出贡献或改进过于复杂的旧代码。 尽管我使用设计模式进行了改进,但我认为它需要作为一个整体进行组织,因此我编写了以下目录作为构成。 内容 生成方式 抽象工厂 建造者 工厂方法 原型 辛格尔顿 结构形态 适配器 桥 合成的 装潢师 正面 轻量级 代理人 行为模式 责任链 命令 口译员 迭代器 -一种集中多个对象之间的协作关系的模式 一种在保持封装的同时创建/管理快照的模式 观察者 状态 战略 模板方法 -一种将数据结构和处理分开的模式 参考书/文字/网站
GOF之23种设计模式的C#实现:DesignPattern.zip
用C#语言实现的GOF之23种设计模式。设计模式的简单实现Demo,仅供学习交流用。本人原创代码,部分参考网络资料。环境:win10 64bit;VS2019;
DesignPattern.Simples.CSharp:23种面向对象设计模式学习示例,C#实现
DesignPattern.Simples.CSharp 23种面向对象设计模式学习示例,C#实现


