设计模式之美

This commit is contained in:
yinkanglong_lab
2021-03-09 21:09:31 +08:00
parent a5132d34cc
commit 26759fc0bd
103 changed files with 7562 additions and 0 deletions

View File

@@ -0,0 +1,176 @@
EntityFramework中使用Repository装饰器
**铺垫**
通常在使用 EntityFramework 时,我们会封装出 IRepository 和 IUnitOfWork
接口,前者负责 CRUD 操作,后者负责数据提交 Commit。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 public interface IRepository\<T\> 2 where T : class 3 { 4 IQueryable\<T\>
Query(); 5 6 void Insert(T entity); 7 8 void Update(T entity, params
Expression\<Func\<T, object\>\>[] modifiedPropertyLambdas); 9 10 void Delete(T
entity); 11 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 public interface IUnitOfWork 2 { 3 void Commit(); 4 }
然后,通过使用 Unity IoC 容器来注册泛型接口与实现类型。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 Func\<IUnityContainer\> factory = () =\> 2 { 3 return new UnityContainer() 4
.RegisterType(typeof(IRepository\<\>), typeof(Repository\<\>), new
ContainerControlledLifetimeManager()) 5 .RegisterType\<IUnitOfWork,
UnitOfWork\>(new ContainerControlledLifetimeManager()) 6
.RegisterType\<DbContext, MyDBContext\>(new
ContainerControlledLifetimeManager()) 7 .RegisterType\<DbContextAdapter\>(new
ContainerControlledLifetimeManager()) 8 .RegisterType\<IObjectSetFactory,
DbContextAdapter\>(new ContainerControlledLifetimeManager()) 9
.RegisterType\<IObjectContext, DbContextAdapter\>(new
ContainerControlledLifetimeManager()); 10 };
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
进而使与数据库相关的操作在 Bisuness Logic 中呈现的非常简单。
例如,通过一系列封装,我们可以达到如下效果:
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 Customer customer = new Customer() 2 { 3 ID = "123",4 FirstName = "Dennis",5
LastName = "Gao",6 }; 7 Repository.Customer.Insert(customer); 8
Repository.Commit();
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
查询操作也是一句话搞定:
1 Customer customer = Repository.Customer.Query().SingleOrDefault(c =\> c.ID ==
"123");
**需求**
假设有一个新的需求:要求在应用层面记录对每个 Table 的 CRUD 的次数。
这时,有几种办法:
1. 应用程序的 Business Logic 中自己记录,比如调用 Update() 操作后记录。
2. 使用 AOP 模式,在调用 CRUD 方法时注入计数器。
3. 修改 Repository\<T\> 实现,在每个方法中嵌入计数器。
4. 继承 Repository\<T\> 类,在衍生类中嵌入计数器。
5. 使用装饰器模式封装 Repository\<T\>,在新的 RepositoryDecorator\<T\>
类中嵌入计数器。
考虑到前三种方法均需要改动已有代码,主要是涉及的修改太多,所有没有尝试采用。
方法 4 则要求修改 Repository\<T\> 的实现,为 CRUD 方法添加 virtual
关键字以便扩展。
方法 5 不需要修改 Repository\<T\> 的实现,对已有代码的改动不大。
综上所述,我们选择了方法 5。
**Repository 装饰器基类实现**
为便于以后的扩展,创建一个装饰器的抽象类。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 public abstract class RepositoryDecorator\<T\> : IRepository\<T\> 2 where T :
class 3 { 4 private readonly IRepository\<T\> \_surrogate; 5 6 protected
RepositoryDecorator(IRepository\<T\> surrogate) 7 { 8 \_surrogate = surrogate; 9
} 10 11 protected IRepository\<T\> Surrogate 12 { 13 get { return \_surrogate; }
14 } 15 16 \#region IRepository\<T\> Members 17 18 public virtual
IQueryable\<T\> Query() 19 { 20 return \_surrogate.Query(); 21 } 22 23 public
virtual void Insert(T entity) 24 { 25 \_surrogate.Insert(entity); 26 } 27 28
public virtual void Update(T entity, params Expression\<Func\<T, object\>\>[]
modifiedPropertyLambdas)29 { 30 \_surrogate.Update(entity,
modifiedPropertyLambdas); 31 } 32 33 public virtual void Delete(T entity) 34 {
35 \_surrogate.Delete(entity); 36 } 37 38 \#endregion39 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
可以看到RepositoryDecorator\<T\> 类型仍然实现了 IRepository\<T\>
接口,对外使用没有任何变化。
**实现需求**
我们定义一个 CountableRepository\<T\> 类用于封装 CRUD 计数功能,其继承自
RepositoryDecorator\<T\> 抽象类。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 public class CountableRepository\<T\> : RepositoryDecorator\<T\> 2 where T :
class 3 { 4 public CountableRepository(IRepository\<T\> surrogate) 5 :
base(surrogate) 6 { 7 } 8 9 public override IQueryable\<T\> Query() 10 { 11
PerformanceCounter.CountQuery\<T\>(); 12 return base.Query();13 } 14 15 public
override void Insert(T entity) 16 { 17 PerformanceCounter.CountInsert\<T\>(); 18
base.Insert(entity);19 } 20 21 public override void Update(T entity, params
Expression\<Func\<T, object\>\>[] modifiedPropertyLambdas)22 { 23
PerformanceCounter.CountUpdate\<T\>(); 24 base.Update(entity,
modifiedPropertyLambdas);25 } 26 27 public override void Delete(T entity) 28 {
29 PerformanceCounter.CountDelete\<T\>(); 30 base.Delete(entity);31 } 32 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
我们在 override 方法中,添加了 CRUD 的计数功能。这里的代码简写为:
1 PerformanceCounter.CountQuery\<T\>();
对原有代码的修改则是需要注册新的 CountableRepository\<T\> 类型。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 Func\<IUnityContainer\> factory = () =\> 2 { 3 return new UnityContainer() 4
.ReplaceBehaviorExtensionsWithSafeExtension() 5
.RegisterType(typeof(IRepository\<\>), typeof(Repository\<\>), new
ContainerControlledLifetimeManager()) 6
.RegisterType(typeof(CountableRepository\<\>), new
ContainerControlledLifetimeManager()) 7 .RegisterType\<IUnitOfWork,
UnitOfWork\>(new ContainerControlledLifetimeManager()) 8
.RegisterType\<DbContext, MyDBContext\>(new
ContainerControlledLifetimeManager()) 9 .RegisterType\<DbContextAdapter\>(new
ContainerControlledLifetimeManager()) 10 .RegisterType\<IObjectSetFactory,
DbContextAdapter\>(new ContainerControlledLifetimeManager()) 11
.RegisterType\<IObjectContext, DbContextAdapter\>(new
ContainerControlledLifetimeManager()); 12 };
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
**扩展应用**
既然有了抽象基类 RepositoryDecorator\<T\> ,我们可以从其设计衍生多个特定场景的
Repository 。
比如,当我们需要为某个 Table 的 Entity 添加缓存功能时,我们可以定制一个
CachableRepository\<T\> 来完成这一个扩展。

View File

@@ -0,0 +1,144 @@
EntityFramework用法探索Repository和UnitOfWork
以上一篇CodeFirst生成代码为基础继续探索使用方式。
引入[Repository模式](http://msdn.microsoft.com/en-us/library/ff649690.aspx)定义最简单的IRepository接口仅包含增删改查接口
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 public interface IRepository\<T\> 2 where T : class3 { 4 IQueryable\<T\>
Query(); 5 void Insert(T entity); 6 void Update(T entity); 7 void Delete(T
entity); 8 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
引入[UnitOfWork模式](http://martinfowler.com/eaaCatalog/unitOfWork.html)因为EntityFramework会负责失败回滚所以此处只定义提交方法。
1 public interface IUnitOfWork 2 { 3 void Commit(); 4 }
实现IRepository接口
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 public class Repository\<T\> : IRepository\<T\> where T : class 2 { 3 private
readonly IObjectSetFactory \_objectSetFactory; 4 private readonly
IObjectSet\<T\> \_objectSet; 5 6 public Repository(IObjectSetFactory
objectSetFactory) 7 { 8 \_objectSetFactory = objectSetFactory; 9 \_objectSet =
objectSetFactory.CreateObjectSet\<T\>(); 10 } 11 12 \#region IRepository\<T\>
Members 13 14 public IQueryable\<T\> Query() 15 { 16 return \_objectSet; 17 } 18
19 public void Insert(T entity) 20 { 21 \_objectSet.AddObject(entity); 22 } 23
24 public void Update(T entity) 25 { 26 \_objectSet.Attach(entity); 27
\_objectSetFactory.ChangeObjectState(entity, EntityState.Modified); 28 } 29 30
public void Delete(T entity) 31 { 32 \_objectSet.DeleteObject(entity); 33 } 34
35 \#endregion36 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
实现IUnitOfWork接口
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 public class UnitOfWork : IUnitOfWork, IDisposable 2 { 3 private readonly
IObjectContext \_objectContext; 4 5 public UnitOfWork(IObjectContext
objectContext) 6 { 7 \_objectContext = objectContext; 8 } 9 10 \#region
IUnitOfWork Members 11 12 public void Commit() 13 { 14
\_objectContext.SaveChanges(); 15 } 16 17 \#endregion18 19 \#region IDisposable
Members 20 21 public void Dispose() 22 { 23 if (\_objectContext != null)24 { 25
\_objectContext.Dispose(); 26 } 27 28 GC.SuppressFinalize(this);29 } 30 31
\#endregion32 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
CustomerRepository类的实现需要做一些配置
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 public CustomerRepository() 2 { 3 Mapper.CreateMap\<DomainModels.Customer,
Customer\>(); 4 Mapper.CreateMap\<Customer, DomainModels.Customer\>(); 5 6
DbContext context = new RETAILContext(); 7 DbContextAdapter contextAdaptor = new
DbContextAdapter(context); 8 9 IObjectSetFactory objectSetFactory =
contextAdaptor; 10 \_repository = new Repository\<Customer\>(objectSetFactory);
11 12 IObjectContext objectContext = contextAdaptor; 13 \_uow = new
UnitOfWork(objectContext); 14 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
则具体增删改查的逻辑实现,
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 public void InsertCustomer(DomainModels.Customer customer) 2 { 3 Customer
entity = Mapper.Map\<DomainModels.Customer, Customer\>(customer); 4 5
\_repository.Insert(entity); 6 \_uow.Commit(); 7 8 customer.Id = entity.Id; 9 }
10 11 public void UpdateCustomer(DomainModels.Customer customer) 12 { 13
Customer entity = \_repository.Query().Single(c =\> c.Id == customer.Id); 14 15
entity.Name = customer.Name; 16 entity.Address = customer.Address; 17
entity.Phone = customer.Phone; 18 19 \_repository.Update(entity); 20 21
\_uow.Commit(); 22 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
在同样的示例下仍然可以工作,
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 ICustomerRepository customerRepository = new CustomerRepository(); 2 3 //
=============== 增 =============== 4 Console.ForegroundColor =
ConsoleColor.DarkRed; 5 6 DomainModels.Customer customer1 = new
DomainModels.Customer() 7 { 8 Name = "Dennis Gao", 9 Address = "Beijing",10
Phone = "18888888888",11 }; 12 customerRepository.InsertCustomer(customer1); 13
Console.WriteLine(customer1);
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
![60f3eef7d5c9.png](media/3d659ede408fbd325ce99f760e43338c.png)
同时UnitOfWork可以保证相关的业务操作在同一个Transaction中
![6ecffe7d2e4c.png](media/33fb368703c7a2f6e4146faae22ef6ad.png)
**完整代码和索引**
EntityFramework用法探索系列
- DatabaseFirst
- CodeFirst
- CodeFirst流畅API
- Repository和UnitOfWork
- 引入Unity
- 静态Repository
- (七)线程安全实践
- (八)事务处理
完整代码下载

View File

@@ -0,0 +1,693 @@
Lambda应用设计模式
**前言**
在使用 Lambda
表达式时,我们常会碰到一些典型的应用场景,而从常用场景中抽取出来的应用方式可以描述为应用模式。这些模式可能不全是新的模式,有的参考自
JavaScript
的设计模式,但至少我看到了一些人为它们打上了名字标签。无论名字的好与坏,我还是决定给这些模式进行命名,至少这些名字很具有描述性。同时我也会给出这些模式的可用性、强大的部分和危险的部分。提前先说明:绝大多数模式是非常强大的,但有可能在代码中引入些潜在的
Bug。所以慎用。
**目录导航**
- *回调模式 Callback Pattern*
- *函数作为返回值 Returning Functions*
- *自定义函数 Self-Defining Functions*
- *立即调用的函数表达式 Immediately-Invoked Function Expression*
- *对象即时初始化 Immediate Object Initialization*
- *初始化时间分支Init-Time Branching*
- *延迟加载 Lazy Loading*
- *属性多态模式 Lambda Property Polymorphism Pattern*
- *函数字典模式 Function Dictionary Pattern*
- *函数式特性 Functional Attribute Pattern*
- *避免循环引用 Avoiding cyclic references*
**回调模式 Callback Pattern**
老生常谈了。事实上,在 .NET
的第一个版本中就已经支持回调模式了,但形式有所不同。现在通过 Lambda
表达式中的闭包和局部变量捕获,这个功能变得越来越有趣了。现在我们的代码可以类似于:
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 void CreateTextBox() 2 { 3 var tb = new TextBox(); 4 tb.IsReadOnly = true; 5
tb.Text = "Please wait ..."; 6 DoSomeStuff(() =\> 7 { 8 tb.Text = string.Empty;
9 tb.IsReadOnly = false;10 }); 11 } 12 13 void DoSomeStuff(Action callback) 14 {
15 // Do some stuff - asynchronous would be helpful ...16 callback(); 17 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
对于 JavaScript
开发人员,这个模式已经没什么新鲜的了。而且通常我们在大量的使用这种模式,因为其非常的有用。例如我们可以使用时间处理器作为参数来处理
AJAX 相关的事件等。在 LINQ 中,我们也使用了这个模式,例如 LINQ 中的 Where
会在每次迭代中回调查询函数。这些仅是些能够说明回调模式非常有用的简单的示例。在
.NET
中,通常推荐使用事件回调机制。原因有两点,一是已经提供了特殊的关键字和类型模式(有两个参数,一个是发送者,一个数事件参数,而发送者通常是
object 类型,而事件参数通常从 EventArgs 继承),同时通过使用 += 和 -+
操作符,也提供了调用多个方法的机会。
**函数作为返回值 Returning Functions**
就像常见的函数一样Lambda
表达式可以返回一个函数指针(委托实例)。这就意味着我们能够使用一个 Lambda
表达式来创建并返回另一个 Lambda
表达式。这种行为在很多场景下都是非常有用的。我们先来看下面这个例子:
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 Func\<string, string\> SayMyName(string language) 2 { 3 switch
(language.ToLower()) 4 { 5 case "fr": 6 return name =\> 7 { 8 return "Je
m'appelle " + name + "."; 9 }; 10 case "de":11 return name =\> 12 { 13 return
"Mein Name ist " + name + ".";14 }; 15 default:16 return name =\> 17 { 18 return
"My name is " + name + ".";19 }; 20 } 21 } 22 23 void Main() 24 { 25 var lang =
"de";26 //Get language - e.g. by current OS settings27 var smn =
SayMyName(lang); 28 var name = Console.ReadLine(); 29 var sentence = smn(name);
30 Console.WriteLine(sentence); 31 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
这段代码可以写的更简洁些。如果请求的语言类型未找到,我们可以直接抛出一个异常,以此来避免返回一个默认值。当然,出于演示的目的,这个例子展示了类似于一种函数工厂。另外一种方式是引入
Hashtable ,或者更好的 Dictionary\<K, V\> 类型。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 static class Translations 2 { 3 static readonly Dictionary\<string,
Func\<string, string\>\> smnFunctions 4 = new Dictionary\<string, Func\<string,
string\>\>(); 5 6 static Translations() 7 { 8 smnFunctions.Add("fr", name =\>
"Je m'appelle " + name + "."); 9 smnFunctions.Add("de", name =\> "Mein Name ist
" + name + ".");10 smnFunctions.Add("en", name =\> "My name is " + name +
".");11 } 12 13 public static Func\<string, string\> GetSayMyName(string
language) 14 { 15 //Check if the language is available has been omitted on
purpose16 return smnFunctions[language]; 17 } 18 } 19 20 // Now it is sufficient
to call Translations.GetSayMyName("de")21 // to get the function with the German
translation.
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
尽管这看起来有点过度设计之嫌,但毕竟这种方式很容易扩展,并且可以应用到很多场景下。如果结合反射一起使用,可以使程序变得更灵活,易于维护,并且更健壮。下面展示了这个模式如何工作:
![bee3cea6685a.png](media/9fba7d170b67e64fbefe8a4822465601.png)
**自定义函数 Self-Defining Functions**
在 JavaScript
中,自定义函数是一种极其常见的设计技巧,并且在某些代码中可以获得更好的性能。这个模式的主要思想就是,将一个函数作为一个属性,而此属性可以被其他函数很容易的更改。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 class SomeClass 2 { 3 public Func\<int\> NextPrime 4 { 5 get; 6 private set; 7
} 8 9 int prime; 10 11 public SomeClass() 12 { 13 NextPrime = () =\> 14 { 15
prime = 2;16 17 NextPrime = () =\> 18 { 19 //Algorithm to determine next -
starting at prime20 //Set prime21 return prime; 22 }; 23 24 return prime; 25 };
26 } 27 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
在这里做了什么呢?首先我们得到了第一个质数,值为
2。这不是重点重点在于我们可以调整算法来排除所有偶数。这在一定程度上会加快我们的算法但我们仍然设置
2 为质数的起点。我们无需看到是否已经调用了 NextPrime()
函数,因为根据函数内的定义会直接返回
2。通过这种方式我们节省了资源并且能够优化算法。
同样,我们也看到了这么做可以性能会更好。让我们来看下下面这个例子:
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 Action\<int\> loopBody = i =\> {2 if(i == 1000)3 loopBody = /\* set to the
body for the rest of the operations \*/;4 5 /\* body for the first 1000
iterations \*/6 }; 7 8 for(int j = 0; j \< 10000000; j++)9 loopBody(j);
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
这里我们有两个截然不同的区域,一个是前 1000 次迭代,另一个是剩下的 9999000
次迭代。通常我们需要一个条件来区分这两种情况。大部分情况下会引起不必要的开销,这也就是我们为什么要使用自定义函数在执行一小段代码后来改变其自身。
**立即调用的函数表达式 Immediately-Invoked Function Expression**
在 JavaScript 中,立即调用函数表达式(简写为 IIFE是非常常见的用法。原因是
JavaScript 并没有使用类似 C\#
中的大括号方式来组织变量的作用域,而是根据函数块来划分的。因此变量会污染全局对象,通常是
window 对象,这并不是我们期待的效果。
解决办法也很简单,尽管大括号并没有给定作用域,但函数给定了,因为在函数体内定义的变量的作用域均被限制在函数内部。而
JavaScript
的使用者通常认为如果那些函数只是为了直接执行,为其中的变量和语句指定名称然后再执行就变成了一种浪费。还有一个原因就是这些函数仅需要执行一次。
在 C\#
中,我们可以简单编写如下的函数达到同样的功能。在这里我们同样也得到了一个全新的作用域,但这并不是我们的主要目的,因为如果需要的话,我们可以在任何地方创建新的作用域。
1 (() =\> { 2 // Do Something here!3 })();
代码看起来很简单。如果我们需要传递一些参数,则需要指定参数的类型。
1 ((string s, int no) =\> { 2 // Do Something here!3 })("Example", 8);
看起来写了这么多行代码并没有给我们带来什么好处。尽管如此,我们可以将这个模式和
async 关键字结合使用。
1 await (async (string s, int no) =\> { 2 // Do Something here async using
Tasks!3 })("Example", 8);4 5 //Continue here after the task has been finished
这样,类似于异步包装器的用法就形成了。
**对象即时初始化 Immediate Object Initialization**
将这个模式包含在这篇文章当中的原因是,匿名对象这个功能太强大了,而且其不仅能包含简单的类型,而且还能包含
Lambda 表达式。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 //Create anonymous object 2 var person = new 3 { 4 Name = "Florian", 5 Age =
28, 6 Ask = (string question) =\> 7 { 8 Console.WriteLine("The answer to \`" +
question + "\` is certainly 42!"); 9 } 10 }; 11 12 //Execute function13
person.Ask("Why are you doing this?");
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
如果你运行了上面这段代码可能你会看到一个异常至少我看到了。原因是Lambda
表达式不能被直接赋予匿名对象。如果你觉得不可思议,那我们的感觉就一样了。幸运的是,编译器告诉了我们“老兄,我不知道我应该为这个
Lambda 表达式创建什么样的委托类型”。既然这样,我们就帮下编译器。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 var person = new2 { 3 Name = "Florian",4 Age = 28,5 Ask =
(Action\<string\>)((string question) =\> 6 { 7 Console.WriteLine("The answer to
\`" + question + "\` is certainly 42!");8 }) 9 };
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
一个问题就出现了这里的函数Ask
方法)的作用域是什么?答案是,它就存活在创建这个匿名对象的类中,或者如果它使用了被捕获变量则存在于其自己的作用域中。所以,编译器仍然创建了一个匿名对象,然后将指向所创建的
Lambda 表达式的委托对象赋值给属性 Ask。
注意:当我们想在匿名对象中直接设定的 Lambda
表达式中访问匿名对象的任一属性时则尽量避免使用这个模式。原因是C\#
编译器要求每个对象在被使用前需要先被声明。在这种情况下,使用肯定在声明之后,但是编译器是怎么知道的?从编译器的角度来看,在这种情况下声明与使用是同时发生的,因此变量
person 还没有被声明。
有一个办法可以帮助我们解决这个问题(实际上办法有很多,但依我的观点,这种方式是最优雅的)。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 dynamic person = null; 2 person = new 3 { 4 Name = "Florian", 5 Age = 28, 6
Ask = (Action\<string\>)((string question) =\> 7 { 8 Console.WriteLine("The
answer to \`" + question + "\` is certainly 42! My age is " + person.Age + ".");
9 }) 10 }; 11 12 //Execute function13 person.Ask("Why are you doing this?");
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
看,现在我们先声明了它。当然我们也可以直接将 person 声明为 object
类型,但通过这种方式我们可以使用反射来访问匿名对象中的属性。此处我们依托于 DLR
Dynamic Language
Runtime来实现这应该是最好的包装方式了。现在这代码看起来很有 JavaScript
范儿了,但实际上我不知道这东西到底有什么用。
**初始化时间分支Init-Time Branching**
这个模式与自定义函数模式密切相关。唯一的不同就是,函数不再定义其自身,而是通过其他函数定义。当然,其他函数也可能没有通过传统的方式去定义,而是通过覆盖属性。
这个模式通常也称为加载时分支Load-Time
Branching本质上是一种优化模式。该模式被用于避免恒定的 switch-case 和 if-else
等控制结构的使用。所以在某种程度上可以说,这种模式为某些恒定代码分支之间建立了联系。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 public Action AutoSave { get; private set; } 2 3 public void
ReadSettings(Settings settings) 4 { 5 /\* Read some settings of the user \*/ 6 7
if (settings.EnableAutoSave) 8 AutoSave = () =\> { /\* Perform Auto Save \*/ };
9 else10 AutoSave = () =\> { }; //Just do nothing!11 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
这里我们做了两件事。首先,我们有一个方法读取了用户设置信息。如果我们发现用于已经打开了自动保存功能,则我们将保存代码赋予该属性。否则我们仅是指定一个空方法。然后,我们就可以一直调用
AutoSave
属性在执行操作。而且在此之后我们不再需要检查用户设置信息了。我们也不需要将这个特定的设置保存到一个
boolean 变量中,因为响应的函数已经被动态的设定了。
你可能说这并没有太大的性能改善,但这只是一个简单的例子。在一些复杂的代码中,这种方法确实可以节省很多时间,尤其是在大循环中调用那个动态设置的方法时。
同时,这样的代码可能更易于维护,并非常易读。在省去了很多不必要的控制过程之后,我们能够直达重点:调用
AutoSave 函数。
在 JavaScript
中,这种模式常用于检测浏览器的功能集。浏览器功能的检测对于任何网站来说都是噩梦一样,而这个模式在实现中就显得非常有用。同样
jQuery 也使用了同样的模式来检测正确的对象,以便使用 AJAX
功能。一旦它识别出浏览器支持 XMLHttpRequest
,则因为浏览器不会在脚本执行期间变化,所以无需在考虑处理 ActiveX 对象了。
**延迟加载 Lazy Loading**
我们想要创建一个对象,它能够执行某种延迟加载操作。也就是说,尽管对象已经被正确地初始化了,但我们并没有加载所有需要的资源。一个原因是想避免在获取需要的数据时引发的大量的
IO
操作。同时,我们也想在准备使用数据时,数据尽可能是最新的。有多种方式可以实现这个功能,而在
Entity Framework 中使用了效率极高的 LINQ
来解决延迟加载的情况。其中IQueryable\<T\>
仅存储了查询而没有存储基础的数据。一旦我们需要这些数据,不仅已构造的查询会被执行,而且查询也是以最高效的形式来执行,例如在远端数据库服务器上执行
SQL 查询语句。
在我们想要的场景中,我们需要区别两种状况。首先我们进行查询,然后后续的操作将在已经获取到的结果上进行。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 class LazyLoad 2 { 3 public LazyLoad() 4 { 5 Search = query =\> 6 { 7 var
source = Database.SearchQuery(query); 8 9 Search = subquery =\> 10 { 11 var
filtered = source.Filter(subquery); 12 13 foreach (var result in filtered) 14
yield return result; 15 }; 16 17 foreach (var result in source) 18 yield return
result; 19 }; 20 } 21 22 public Func\<string, IEnumerable\<ResultObject\>\>
Search { get; private set; }23 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
那么,在这里基本上我们需要设置两个不同的方法。一个是从数据库拉数据,另一个是从已获取到的数据中进行过滤。当然你可能会想我们也可以在类中创建另一个方法来设置这些行为或者使用其他方式可能更有效。
**属性多态模式 Lambda Property Polymorphism Pattern**
Lambda表达式可以被用于实现多态override而不需要使用 abstract 和 virtual
等关键字。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 class MyBaseClass 2 { 3 public Action SomeAction { get; protected set; } 4 5
public MyBaseClass() 6 { 7 SomeAction = () =\> 8 { 9 //Do something!10 }; 11 }
12 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
这里没什么特别的。我们创建了一个类,通过一个属性来暴露一个函数。这有点像
JavaScript
风格。有趣的地方不仅在于可以在这个类中控制和更改这个函数属性,而且可以在它的衍生类中更改。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 class MyInheritedClass : MyBaseClass 2 { 3 public MyInheritedClass 4 { 5
SomeAction = () =\> { 6 //Do something different!7 }; 8 } 9 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
看!实际上这里我们能够更改这个属性完全是依赖于 protected
的应用。这种方式的缺点是我们无法直接访问父类的实现。这里我们丢失了 base
的强大能力,因为 base
中的属性具有相同的值。如果你确实还需要这样的功能,我建议使用下面这种“模式”:
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 class MyBaseClass 2 { 3 public Action SomeAction { get; private set; } 4 5
Stack\<Action\> previousActions; 6 7 protected void AddSomeAction(Action
newMethod) 8 { 9 previousActions.Push(SomeAction); 10 SomeAction = newMethod; 11
} 12 13 protected void RemoveSomeAction() 14 { 15 if (previousActions.Count ==
0)16 return;17 18 SomeAction = previousActions.Pop(); 19 } 20 21 public
MyBaseClass() 22 { 23 previousActions = new Stack\<Action\>(); 24 25 SomeAction
= () =\> 26 { 27 //Do something!28 }; 29 } 30 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
这样,在子类中只能调用 AddSomeAction()
来覆写当前已设置的方法。这个方法将被直接放入一个栈内,这使我们能够记录之前的状态。
我给这个模式起的名字是 Lambda属性多态模式Lambda Property Polymorphism
Pattern。它主要描述将函数封装为属性的可能性然后能够在衍生类中覆写父类的属性。上面代码中的栈只是一个额外的功能并不会改变这个模式的目标。
为什么需要这个模式?坦白的说,有多种原因。首先就是因为我们能这么做。但要注意,实际上如果我们要使用多个不同的属性时,这个模式会变得更灵活。“多态”这个词也就有了全新的含义,但那就是另一个模式了。所以这里我主要是想强调这个模式可以实现一些以前曾认为不可能的功能。
例如:你想覆写一个静态方法(不推荐这么做,但或许这么做是能解决你的问题的最优雅的方法)。那么,继承是不可能改变静态方法的。原因很简单:继承仅应用于类的实例,而静态方法却没有被绑定到类的实例上。静态方法对所有的类的实例都是相同的。这里也蕴含着一个警告,下面的这个模式可能不没有达到你想要的结果,所以一定要明确你为什么要这么用。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 void Main() 2 { 3 var mother = HotDaughter.Activator().Message; 4 //mother =
"I am the mother" 5 var create = new HotDaughter(); 6 var daughter =
HotDaughter.Activator().Message; 7 //daughter = "I am the daughter" 8 } 9 10
class CoolMother 11 { 12 public static Func\<CoolMother\> Activator { get;
protected set; }13 14 //We are only doing this to avoid NULL references!15
static CoolMother() 16 { 17 Activator = () =\> new CoolMother(); 18 } 19 20
public CoolMother() 21 { 22 //Message of every mother23 Message = "I am the
mother";24 } 25 26 public string Message { get; protected set; }27 } 28 29 class
HotDaughter : CoolMother 30 { 31 public HotDaughter() 32 { 33 //Once this
constructor has been "touched" we set the Activator ...34 Activator = () =\> new
HotDaughter(); 35 //Message of every daughter36 Message = "I am the daughter";37
} 38 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
这是一个极其简单的示例,并且希望不要引起误导。如果这么用可能会导致事情变的更复杂,所以我一直说为什么我们需要避免这么用,只是描述了其可行性。关于静态多态的较好的方案总不是易于实现的,并且需要很多的代码,所以除非它真能帮你解决实际的问题,而不是让你更头痛。
**函数字典模式 Function Dictionary Pattern**
之前我已经介绍了这个模式只是还没有指定名字它就是函数字典模式Function
Dictionary
Pattern。这个模式的基本成分包括一个哈希表或字典用于包含一些键值对键可能是任意类型值是某些类型的函数。这个模式也指定了一个特殊的字典构造方式。这在这个模式中是必须的否则只能使用
switch-case 来达到相同的目的了。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 public Action GetFinalizer(string input) 2 { 3 switch 4 { 5 case "random": 6
return () =\> { /\* ... \*/ }; 7 case "dynamic": 8 return () =\> { /\* ... \*/
}; 9 default:10 return () =\> { /\* ... \*/ }; 11 } 12 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
上面代码中我们需要一个字典类型吗?当然。我们可以这么做:
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 Dictionary\<string, Action\> finalizers; 2 3 public void BuildFinalizers() 4 {
5 finalizers = new Dictionary\<string, Action\>(); 6 finalizers.Add("random", ()
=\> { /\* ... \*/ }); 7 finalizers.Add("dynamic", () =\> { /\* ... \*/ }); 8 } 9
10 public Action GetFinalizer(string input) 11 { 12
if(finalizers.ContainsKey(input))13 return finalizers[input]; 14 15 return ()
=\> { /\* ... \*/ }; 16 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
但要注意,在这里使用这个模式并没有带来任何好处。实际上,这个模式的效率更低,并且需要更多格外的代码。但是我们能做的事情是,通过反射来是函数字典的构造过程自动化。同样还是没有使用
switch-case
语句的效率高,但代码更健壮,可维护性更高。实际上这个操作也很方便,比如我们有大量的代码,我们甚至不知道在哪个方法内加入
switch-case 代码块。
我们来看一个可能的实现。通常我会建议在代码中增加一些约定,以便能够得到字典的键。当然,我们也可以通过选择类中某个属性的名称,或者直接使用方法的名称来满足需求。在下面的示例中,我们仅选择一种约定:
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 static Dictionary\<string, Action\> finalizers; 2 3 //The method should be
called by a static constructor or something similar 4 //The only requirement is
that we built 5 public static void BuildFinalizers() 6 { 7 finalizers = new
Dictionary\<string, Action\>(); 8 9 //Get all types of the current (= where the
code is contained) assembly10 var types =
Assembly.GetExecutingAssembly().GetTypes(); 11 12 foreach (var type in types) 13
{ 14 //We check if the class is of a certain type15 if
(type.IsSubclassOf(typeof(MyMotherClass)))16 { 17 //Get the constructor18 var m
= type.GetConstructor(Type.EmptyTypes); 19 20 //If there is an empty constructor
invoke it21 if (m != null)22 { 23 var instance = m.Invoke(null) as
MyMotherClass; 24 //Apply the convention to get the name - in this case just we
pretend it is as simple as25 var name = type.Name.Remove("Mother");26 //Name
could be different, but let's just pretend the method is named MyMethod27 var
method = instance.MyMethod; 28 29 finalizers.Add(name, method); 30 } 31 } 32 }
33 } 34 35 public Action GetFinalizer(string input) 36 { 37 if
(finalizers.ContainsKey(input)) 38 return finalizers[input]; 39 40 return () =\>
{ /\* ... \*/ }; 41 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
现在这段代码是不是更好些呢。事实上,这个模式可以节省很多工作。而其中最好的就是:它允许你实现类似插件的模式,并且使此功能跨程序集应用。为什么这么说呢?比如我们可以扫描指定模式的类库,并将其加入到代码中。通过这种方式也可以将其他类库中的功能添加到当前代码中。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 //The start is the same 2 3 internal static void BuildInitialFinalizers() 4 {
5 finalizers = new Dictionary\<string, Action\>(); 6
LoadPlugin(Assembly.GetExecutingAssembly()); 7 } 8 9 public static void
LoadPlugin(Assembly assembly) 10 { 11 //This line has changed12 var types =
assembly.GetTypes(); 13 14 //The rest is identical! Perfectly refactored and
obtained a new useful method15 foreach (var type in types) 16 { 17 if
(type.IsSubclassOf(typeof(MyMotherClass)))18 { 19 var m =
type.GetConstructor(Type.EmptyTypes); 20 21 if (m != null)22 { 23 var instance =
m.Invoke(null) as MyMotherClass; 24 var name = type.Name.Remove("Mother");25 var
method = instance.MyMethod; 26 finalizers.Add(name, method); 27 } 28 } 29 } 30 }
31 32 //The call is the same
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
现在我们仅需要通过一个点来指定插件。最后将会从某路径中读取类库,尝试创建程序集对象,然后调用
LoadPlugin() 来加载程序集。
**函数式特性 Functional Attribute Pattern**
Attribute 是 C\# 语言中最棒的功能之一。借助 Attribute曾在 C/C++
中不太容易实现的功能在C\#中仅需少量的代码即可实现。 这个模式将 Attribute 与
Lambda 表达式结合到一起。在最后函数式特性模式Functional Attribute
Pattern将会提高 Attribute 应用的可能性和生产力。
可以说,将 Lambda 表达式和 Attribute
结合到也一起相当的有帮助,因为我们不再需要编写特定的类。让我们来看个例子来具体解释是什么意思。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 class MyClass 2 { 3 public bool MyProperty 4 { 5 get;6 set;7 } 8 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
现在针对这个类的实例,我们想要能够根据一些领域特性语言或脚本语言来改变这个属性。然后我们还想能够在不写任何额外代码的条件下来改变属性的值。当然,我们还是需要一些反射机制。同时也需要一些
attribute 来指定是否这个属性值能够被用户更改。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 class MyClass 2 { 3 [NumberToBooleanConverter] 4 [StringToBooleanConverter] 5
public bool MyProperty 6 { 7 get; 8 set; 9 } 10 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
我们定义两种转换器。虽然使用一个即可标示这个属性可以被任何用于更改。我们使用两个来为使用者提供更多的可能性。在这个场景下一个使用者可能实际上使用一个字符串来设置这个值将字符串转换成布尔值或者用一个数字比如0或1
那么这些转换器如何实现呢?我们来看下 StringToBooleanConverterAttribute 的实现。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 public class StringToBooleanConverterAttribute : ValueConverterAttribute 2 { 3
public StringToBooleanConverterAttribute() 4 : base(typeof(string), v =\> { 5
var str = (v as string ?? string.Empty).ToLower(); 6 7 if (str == "on") 8 return
true; 9 else if (str == "off")10 return false;11 12 throw new Exception("The
only valid input arguments are [ on, off ]. You entered " + str + ".");13 }) 14
{ 15 /\* Nothing here on purpose \*/16 } 17 } 18 19 public abstract class
ValueConverterAttribute : Attribute 20 { 21 public ValueConverterAttribute(Type
expected, Func\<object, object\> converter)22 { 23 Converter = converter; 24
Expected = expected; 25 } 26 27 public ValueConverterAttribute(Type expected) 28
{ 29 Expected = expected; 30 } 31 32 public Func\<Value, object\> Converter {
get; set; }33 34 public object Convert(object argument) 35 { 36 return
Converter.Invoke(argument); 37 } 38 39 public bool CanConvertFrom(object
argument) 40 { 41 return Expected.IsInstanceOfType(argument); 42 } 43 44 public
Type Expected 45 { 46 get;47 set;48 } 49 50 public string Type 51 { 52 get {
return Expected.Name; } 53 } 54 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
使用这个模式我们得到了什么好处呢?如果 Attribute
能够接受非常量表达式作为参数比如委托、Lambda
表达式等都有可能),则我们得到的好处会更多。通过这种方式,我们仅需使用 Lambda
表达式来替换抽象方法,然后将其传递给父类的构造函数。
你可能有些意见,这和 abstract
函数比并没什么新鲜的,但有趣的地方在于不能像使用函数一样来用,而是作为一个属性能够被外部进行设置。这可以被用于一些动态代码中,来重写一些转换器,尽管其已经被实例化了。
**避免循环引用 Avoiding cyclic references**
C\#中,循环引用并不是一个大问题。实际上仅在一种方式下会使循环引用带来问题,那就是在
struct
结构体中。因为类是引用类型,循环引用并没有什么坏处。在源对象上持有目标对象的一个引用指针,而在目标对象上持有一个源对象的引用指针,这不会有任何问题。
但是如果是结构体,我们没法使用指针,其在栈上创建对象。因为在这种情况下,若源对象包含一个目标对象,实际上是包含了一个目标对象的拷贝,而不是真正的目标对象,而反过来也一样。
大部分情况下,编译器会检测到这种循环引用,然后抛出一个编译错误,这个功能其实很棒。我们来看个能引起错误的例子:
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 struct FirstStruct 2 { 3 public SecondStruct Target; 4 } 5 6 struct
SecondStruct 7 { 8 public FirstStruct Source; 9 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
这上面的代码中,使用结构体变量。这与类有巨大的不同:尽管我们没初始化变量,但变量其实已经被初始化为默认值。
所以说,编程是件复杂的事,编译器也不是万能的神。通过一些方式可以骗过编译器。如果我们欺骗了编译器,编译器就会告诉我们一个运行时错误,无法创建这个对象。一种欺骗方式是使用自动属性:
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 struct FirstStruct 2 { 3 public SecondStruct Target { get; set; }4 } 5 6
struct SecondStruct 7 { 8 public FirstStruct Source { get; set; }9 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
这不会阻止问题的发生其只是将问题从编译时错误延迟到了运行时错误。我们脑中立即会产生一个方案就是使用可空结构nullable
struct
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 struct FirstStruct 2 { 3 public SecondStruct? Target { get; set; }4 } 5 6
struct SecondStruct 7 { 8 public FirstStruct Source { get; set; }9 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
这里的问题是,那些可空结构也同样是结构体,他们继承自 System.Nullable\<T\>
,实际上也是一个结构体类型。
终于Lambda表达式来拯救我们了。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 struct FirstStruct 2 { 3 readonly Func\<SecondStruct\> f; 4 5 public
FirstStruct(SecondStruct target) 6 { 7 f = () =\> target; 8 } 9 10 public
SecondStruct Target 11 { 12 get13 { 14 return f(); 15 } 16 } 17 } 18 19 struct
SecondStruct 20 { 21 public FirstStruct Source { get; set; }22 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
这里我们做了什么呢?我们使用了一个对函数的引用,而该函数会返回给我们结构体。编译器会生成一个类来包含这个结构体,这样这个结构体就作为一个全局变量存在了。因为结构体总是会包含一个默认的构造函数,会保持
f 的未引用状态,我们加了另一个构造函数,并且将目标结构体作为参数传入。
最后我们创建了一个闭包在其中返回被捕获的结构体实例。重点的强调下可能会有其他可能性。如果使用一个引用类型作为值类型的容器可能循环引用的情况更糟。Lambda
表达式只是能完成这个功能的一种方式,但在某些条件下,其是能处理这种场景的最具表达性和最直接的方式。
**完整代码**
![edBlockStart.gif](media/405b18b4b6584ae338e0f6ecaf736533.gif)
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 class TestPatterns 2 { 3 public static void SelfDefining() 4 { 5
Console.WriteLine(":: Pattern: Self-definining function"); 6 7 Action foo = ()
=\> 8 { 9 Console.WriteLine("Hi there!"); 10 11 foo = () =\> 12 { 13
Console.WriteLine("Hi again!"); 14 }; 15 }; 16 17 Console.WriteLine("First call
(initilization)."); 18 foo(); 19 Console.WriteLine("Second call - use different
one now!"); 20 foo(); 21 Console.WriteLine("Third call - still the same."); 22
foo(); 23 } 24 25 public static void Callback() 26 { 27 Console.WriteLine("::
Pattern: Callback pattern"); 28 Console.WriteLine("Calling the function with
lambda expression."); 29 30 CallMe(() =\> "The boss."); 31 32
Console.WriteLine("Back at the starting point."); 33 } 34 35 static void
CallMe(Func\<string\> caller) 36 { 37 Console.WriteLine("Received function as
parameter - Who called?!"); 38 Console.WriteLine(caller()); 39 } 40 41 public
static void Returning() 42 { 43 Console.WriteLine(":: Pattern: Returning
function"); 44 Console.WriteLine("Calling to obtain the method ..."); 45
Func\<double, double\> method = GetProperMethod("sin"); 46
Console.WriteLine("Doing something with the method ..."); 47
Console.WriteLine("f(pi / 4) = {0}", method(Math.PI / 4)); 48 } 49 50 static
Func\<double, double\> GetProperMethod(string what) 51 { 52 switch (what) 53 {
54 case "sin": 55 return Math.Sin; 56 57 case "cos": 58 return Math.Cos; 59 60
case "exp": 61 return Math.Exp; 62 63 default: 64 return x =\> x; 65 } 66 } 67
68 public static void IIFE() 69 { 70 Console.WriteLine(":: Pattern: IIFE"); 71
72 ((Action\<double\>)((x) =\> 73 { 74 Console.WriteLine(2.0 \* x \* x - 0.5 \*
x); 75 }))(1.0); 76 77 78 ((Action\<double, double\>)((x, y) =\> 79 { 80
Console.WriteLine(2.0 \* x \* y - 1.5 \* x); 81 }))(2.0, 3.0); 82 } 83 84 public
static void ImmediateObject() 85 { 86 Console.WriteLine(":: Pattern: Immediate
object initialization"); 87 88 var terminator = new 89 { 90 Typ = "T1000", 91
Health = 100, 92 Hit = (Func\<double, double\>)((x) =\> 93 { 94 return 100.0 \*
Math.Exp(-x); 95 }) 96 }; 97 98 Console.WriteLine("Terminator with type {0} has
been created.", terminator.Typ); 99 Console.WriteLine("Let's hit the terminator
with 0.5. Rest health would be {0}!", terminator.Hit(0.5));100 } 101 102 public
static void InitTimeBranching() 103 { 104 Console.WriteLine(":: Pattern:
Init-time branching");105 Action\<int\> loopBody = null;106
Console.WriteLine("Select a proper loop body method ...");107 Random r = new
Random(); 108 int sum = 0;109 110 if (r.NextDouble() \< 0.5)111 { 112
Console.WriteLine("Selected random choice ...");113 114 loopBody = index =\> 115
{ 116 sum += r.Next(0, 10000);117 }; 118 } 119 else120 { 121
Console.WriteLine("Selected little gauss ...");122 123 loopBody = index =\> 124
{ 125 sum += index; 126 }; 127 } 128 129 Console.WriteLine("Execute the loop
...");130 131 for (var i = 0; i \< 10000; i++)132 loopBody(i); 133 134
Console.WriteLine("Loop has finished with result sum = {0}.", sum);135 } 136 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
**文章内容翻译并改编自** [**Way to
Lambda**](http://www.codeproject.com/Articles/507985/Way-to-Lambda)
**,章节和代码有很大的改动,未包含全部内容。**

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -0,0 +1,183 @@
单一职责原则Single Responsibility Principle
**单一职责原则SRPThe Single Responsibility Principle**
> 一个类应该有且只有一个变化的原因。
> **There should never be more than one reason for a class to change.**
![05479994772.jpeg](media/4ac1f99f76e6d199ef34ae1fb3f26ba2.jpeg)
为什么将不同的职责分离到单独的类中是如此的重要呢?
因为每一个职责都是一个变化的中心。当需求变化时,这个变化将通过更改职责相关的类来体现。
如果一个类拥有多于一个的职责,则这些职责就耦合到在了一起,那么就会有多于一个原因来导致这个类的变化。对于某一职责的更改可能会损害类满足其他耦合职责的能力。这样职责的耦合会导致设计的脆弱,以至于当职责发生更改时产生无法预期的破坏。
例如,考虑下图中的设计。类图中显示 Rectangle
类包含两个方法一个方法Draw负责在显示屏幕上绘制矩形另一个方法Area负责计算矩形图形面积。
![818121707390.png](media/f8bea867abcf60fc1549e6ecf2748c58.png)
有两个不同的应用程序均使用了 Rectangle 类。一个应用为计算几何程序,它使用了
Rectangle
中的数学几何模型但不会在显示屏幕上绘制矩形。另一个应用是一个图形界面程序GUI它可能会做一些计算几何方面的工作但主要功能是在屏幕上绘制矩形。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 public class Rectangle 2 { 3 public int Height { get; set; } 4 public int
Width { get; set; } 5 6 public double Area() 7 { 8 return Width \* Height; 9 }
10 11 public void Draw(Form form) 12 { 13 SolidBrush brush = new
SolidBrush(Color.Red); 14 Graphics formGraphics = form.CreateGraphics(); 15
formGraphics.FillRectangle(brush, 16 new System.Drawing.Rectangle( 17 new
Point(0, 0), new Size(Width, Height))); 18 } 19 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
这个设计侵犯了 SRP 原则。Rectangle
类包含了两个职责。第一个职责是提供矩形几何计算的数学模型,第二个职责是在 GUI
上渲染矩形。
对 SRP 原则的侵犯会导致诸多难以解决的问题:
首先,我们必须在计算几何应用中包含对 GUI
库的引用。这导致应用程序无谓的消耗了链接时间、编译时间、内存空间和存储空间等。
再者,如果因为某些原因对 GraphicalApplication 的一个更改导致 Rectangle
类也相应做了更改,这将强制我们对 ComputationalGeometryApplication
进行重新编译、重新测试和重新部署等。如果我们忘了做这些事情,那么应用程序可能以无法预期的方式而崩溃。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 public class ComputationalGeometryApplication 2 { 3 public double
CalculateArea(Rectangle rectangle) 4 { 5 double area = rectangle.Area(); 6
return area; 7 } 8 } 9 10 public class GraphicalApplication 11 { 12 public Form
form { get; set; }13 14 public void DrawOnScreen(Rectangle rectangle) 15 { 16
rectangle.Draw(form); 17 } 18 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
一个较好的设计是将这两个职责完全地隔离到不同的类当中,如下图所示。这个设计将
Rectangle 中关于几何计算的职责移到了 GeometricRectangle 类中,而 Rectangle
类中仅保留矩形渲染职责。
![820070453862.png](media/d757c7f18fd48bc5989ba6d1fb01b253.png)
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 public class GeometricRectangle 2 { 3 public int Height { get; set; } 4 public
int Width { get; set; } 5 6 public double Area() 7 { 8 return Width \* Height; 9
} 10 } 11 12 public class Rectangle 13 { 14 public void Draw(Form form,
GeometricRectangle geometric) 15 { 16 SolidBrush brush = new
SolidBrush(Color.Red); 17 Graphics formGraphics = form.CreateGraphics(); 18
formGraphics.FillRectangle(brush, 19 new System.Drawing.Rectangle( 20 new
Point(0, 0),21 new Size(geometric.Width, geometric.Height))); 22 } 23 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
然后,如果我们再对 Rectangle 中渲染职责进行更改时将不会再影响到
ComputationalGeometryApplication 了。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 public class ComputationalGeometryApplication 2 { 3 public double
CalculateArea(GeometricRectangle geometric) 4 { 5 double area =
geometric.Area(); 6 return area; 7 } 8 } 9 10 public class GraphicalApplication
11 { 12 public Form form { get; set; }13 14 public void DrawOnScreen(Rectangle
rectangleDraw, GeometricRectangle rectangleShape) 15 { 16
rectangleDraw.Draw(form, rectangleShape); 17 } 18 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
**那么职责Responsibility到底是什么**
**在**[**单一职责原则SRPSingle Responsibility
Principle**](http://www.cnblogs.com/gaochundong/p/single_responsibility_principle.html)**的概念中我们将职责Responsibility定义为
"一个变化的原因a reason for
change"。如果你能想出多于一种动机来更改一个类,则这个类就包含多于一个职责。**
职责的耦合有时很难被发现,因为我们习惯于将多个职责一起来考虑。例如,我们考虑下面定义的
Camera 接口,可能会认为这个接口看起来是非常合理的。接口中声明的 4
个方法从属于一个 Camera 接口定义。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 public interface Camera 2 { 3 void Connect(string host); 4 void Disconnect();
5 void Send(byte[] data);6 byte[] Receive();7 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
然而,它确实耦合了 2 个职责。第一个职责是连接管理第二个职责是数据通信。Connect
和 Disconnect 方法负责管理 Camera 与管理端 Host 的连接,而 Send 和 Receive
方法则负责收发通信数据。
这两个职责应该被分离吗?答案基本上是肯定的。这两组方法基本上没有任何交集,它们都可以依据不同的原因而变化。进一步说,它们将在应用程序中完全不同的位置被调用,而那些不同的位置将同样会因不同的原因而变化。
因此,下图中的设计可能会好一些。它将这两个职责分别隔离到不同的接口定义中,这至少使应用程序从两个职责中解耦。
![840177804675.png](media/6a7e900756c3af21380c9575cb0a6941.png)
然而,我们注意到这两个职责又重新被耦合进了一个 CameraImplementation
类中。这可能不是我们想要的,但却有可能是必须的。通常有很多原因会强制我们将一些职责耦合在一起。尽管如此,我们使得应用程序的其他部分得益于这个接口的隔离。
CameraImplementation
类在我们看来是一个组合出来的但确实包含一些缺点的类。但需要注意到的是,所有其他需要使用
CameraImplementation
类的地方已经可以被接口进行隔离,我们仅需依赖所定义的单一职责的接口。而
CameraImplementation
仅在被实例化的位置才会出现。我们将丑陋的代码限制在一定的范围内,而不会泄露或污染应用程序的其他部分。
**总结**
单一职责原则SRPSingle Responsibility Principle可表述为
"一个类应该有且只有一个变化的原因There should never be more than one reason
for a class to
change."。单一职责原则是一个非常简单的原则,但通常也是最难做的正确的一个原则。职责的联合是在实践中经常碰到的事情,从这些各不相同的职责中发现并隔离职责就是软件设计的真谛所在。我们所讨论的其他设计原则最终也会回归到这个原则上来。
**面向对象设计的原则**
| SRP | [单一职责原则](http://www.cnblogs.com/gaochundong/p/single_responsibility_principle.html) | [Single Responsibility Principle](http://www.cnblogs.com/gaochundong/p/single_responsibility_principle.html) |
|------|--------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------|
| OCP | [开放封闭原则](http://www.cnblogs.com/gaochundong/p/open_closed_principle.html) | [Open Closed Principle](http://www.cnblogs.com/gaochundong/p/open_closed_principle.html) |
| LSP | [里氏替换原则](http://www.cnblogs.com/gaochundong/p/liskov_substitution_principle.html) | Liskov Substitution Principle |
| ISP | [接口分离原则](http://www.cnblogs.com/gaochundong/p/interface_segregation_principle.html) | [Interface Segregation Principle](http://www.cnblogs.com/gaochundong/p/interface_segregation_principle.html) |
| DIP | [依赖倒置原则](http://www.cnblogs.com/gaochundong/p/dependency_inversion_principle.html) | [Dependency Inversion Principle](http://www.cnblogs.com/gaochundong/p/dependency_inversion_principle.html) |
| LKP | [最少知识原则](http://www.cnblogs.com/gaochundong/p/least_knowledge_principle.html) | [Least Knowledge Principle](http://www.cnblogs.com/gaochundong/p/least_knowledge_principle.html) |
**参考资料**
- *SRPThe Single Responsibility Principle by Robert C. Martin “Uncle Bob”*
- *The SOLID Principles, Explained with Motivational Posters*
- *Dangers of Violating SOLID Principles in C\#*
- *An introduction to the SOLID principles of OO design*
- *10 Golden Rules Of Good OOP*
- *10 Object Oriented Design principles Java programmer should know*
- *SOLID Principles: Single Responsibility Principle*
- *Object Oriented Design Principles*

View File

@@ -0,0 +1,280 @@
开放封闭原则Open Closed Principle
在面向对象的设计中有很多流行的思想,比如说
"所有的成员变量都应该设置为私有Private""要避免使用全局变量Global
Variables""使用运行时类型识别RTTIRun Time Type Identification例如
dynamic_cast是危险的"
等等。那么这些思想的源泉是什么为什么它们要这样定义这些思想总是正确的吗本篇文章将介绍这些思想的基础开放封闭原则Open
Closed Principle
Ivar Jacobson 曾说过
"所有系统在其生命周期中都会进行变化,只要系统要开发一个版本以上这一点就需时刻记住。"。
> All systems change during their life cycles. This must be borne in mind when
> developing systems expected to last longer than the first version.
那么我们到底如何才能构建一个稳定的设计来面对这些变化,以使软件生命周期持续的更长呢?
早在1988年Bertrand Meyer
就给出了指导建议,他创造了当下非常著名的开放封闭原则。套用他的原话:"软件实体(类、模块、函数等)应对扩展开放,但对修改封闭。"。
> **Software entities (classes, modules, functions, etc.) should be open for
> extension, but closed for modification.**
当一个需求变化导致程序中多个依赖模块都发生了级联的改动,那么这个程序就展现出了我们所说的
"坏设计bad design"
的特质。应用程序也相应地变得脆弱、僵化、无法预期和无法重用。开放封闭原则Open
Closed
Principle即为解决这些问题而产生它强调的是你设计的模块应该从不改变。当需求变化时你可以通过添加新的代码来扩展这个模块的行为而不去更改那些已经存在的可以工作的代码。
**开放封闭原则Open Closed Principle描述**
符合开放封闭原则的模块都有两个主要特性:
**1. 它们 "面向扩展开放Open For Extension"。**
也就是说模块的行为是能够被扩展的。当应用程序的需求变化时,我们可以使模块表现出全新的或与以往不同的行为,以满足新的需求。
**2. 它们 "面向修改封闭Closed For Modification"。**
模块的源代码是不能被侵犯的,任何人都不允许修改已有源代码。
![17336555168.jpeg](media/e34e3723a1d1f114c83da1351b80724a.jpeg)
看起来上述两个特性是互相冲突的,因为通常扩展模块行为的常规方式就是修改该模块。一个不能被修改的模块通常被认为其拥有着固定的行为。那么如何使这两个相反的特性共存呢?
抽象是关键。
> **Abstraction is the Key.**
在使用面向对象设计技术时,可以创建固定的抽象和一组无限界的可能行为来表述。这里的抽象指的是抽象基类,而无限界的可能行为则由诸多可能衍生出的子类来表示。为了一个模块而篡改一个抽象类是有可能的,而这样的模块则可以对修改封闭,因为它依赖于一个固定的抽象。然后这个模块的行为可以通过创建抽象的衍生类来扩展。
**示例Client/Server 引用**
图1 展示了一个简单的且不符合开放封闭原则的设计。
![102360778770.png](media/0d3edc3fdc41b5a9178d24327f569bfc.png)
(图 1 封闭的 Client
Client 和 Server 类都是具体类Concrete Class所以无法保证 Server
的成员函数是虚函数。 这里 Client 类使用了 Server 类。如果我们想让 Client
对象使用一个不同的 Server 对象,那么必须修改 Client 类以使用新的 Server
类和对象。
图 2 中展示了符合开放封闭原则的相应设计。
![106097807482.png](media/fa6446797ee1d21a2cdf19e23439008a.png)
(图 2 开放的 Client
在这个示例中AbstractServer 类是一个抽象类并包含一个纯虚成员函数。Client
类依赖了这个抽象,但 Client 类将使用衍生的 Server 类的对象实例。如果我们需要
Client 对象使用一个不同的 Server 类,则可以从 AbstractServer
类衍生出一个新的子类,而 Client 类则依然保持不变。
**示例Shape 抽象**
考虑下面这个例子。我们有一个应用程序需要在标准 GUI
窗口上绘制圆形Circle和方形Square。圆形和方形必须以特定的顺序进行绘制。圆形和方形会被创建在同一个列表中并保持适当的顺序而程序必须能够顺序遍历列表并绘制所有的圆形和方形。
在 C
语言中,使用过程化技术是无法满足开放封闭原则的。我们可能会通过下面代码显示的方式来解决该问题。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 enum ShapeType {circle, square}; 2 3 struct Shape 4 { 5 ShapeType itsType; 6
}; 7 8 struct Square 9 { 10 ShapeType itsType; 11 double itsSide; 12 Point
itsTopLeft; 13 }; 14 15 struct Circle 16 { 17 ShapeType itsType; 18 double
itsRadius; 19 Point itsCenter; 20 }; 21 22 void DrawSquare(struct Square\*); 23
void DrawCircle(struct Circle\*); 24 typedef struct Shape \*ShapePointer; 25 26
void DrawAllShapes(ShapePointer list[], int n) 27 { 28 int i; 29 for (i=0; i\<n;
i++)30 { 31 struct Shape\* s = list[i]; 32 switch (s-\>itsType) 33 { 34 case
square: 35 DrawSquare((struct Square\*)s); 36 break;37 case circle: 38
DrawCircle((struct Circle\*)s); 39 break;40 } 41 } 42 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
在这里我们看到了一组数据结构定义这些结构中除了第一元素相同外其他都不同。通过第一个元素的类型码来识别该结构是在表示一个圆形Circle还是一个方形Square。函数
DrawAllShapes 遍历了数组中的结构指针检查类型码然后调用相匹配的函数DrawCircle
或 DrawSquare
这里函数 DrawAllShapes 不符合开放封闭原则,因为它无法保证对新的 Shape
种类保持封闭。如果我们想要扩展这个函数使其能够支持一个图形列表并且包含三角形Triangle定义则我们将不得不修改这个函数。事实上每当我们需要绘制新的图形种类时我们都不得不修改这个函数。
当然这个程序仅仅是一个例子。在实践中 DrawAllShapes 函数中的 switch
语句将不断地在应用程序内的各种函数间不断的调用,而每个函数只是少许有些不同。在这样的应用中增加一个新的
Shape 意味着需要搜寻所有类似的 switch 语句(或者是 if/else
链)存在的地方,然后增加新的 Shape 功能。此外,要让所有的 switch 语句(或者是
if/else 链)都有类似 DrawAllShapes
函数这样较好的结构也是不太可能的。而更有可能的则是 if
语句将和一些逻辑运算符绑定到了一起,或者 switch 语句中的 case
子句的堆叠。因此要在所有的位置找到和理解这些问题,然后添加新的图形定义可不是件简单的事情。
下面这段代码展示了符合开放封闭原则的 Cicle/Square 问题的一个解决方案。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 public abstract class Shape 2 { 3 public abstract void Draw(); 4 } 5 6 public
class Circle : Shape 7 { 8 public override void Draw() 9 { 10 // draw circle on
GUI11 } 12 } 13 14 public class Square : Shape 15 { 16 public override void
Draw() 17 { 18 // draw square on GUI19 } 20 } 21 22 public class Client 23 { 24
public void DrawAllShapes(List\<Shape\> shapes) 25 { 26 foreach (var shape in
shapes) 27 { 28 shape.Draw(); 29 } 30 } 31 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
在这个例子中,我们创建了一个 Shape 抽象类,这个抽象类包含一个纯虚函数 Draw。而
Circle 和 Square 都衍生自 Shape 类。
注意在这里如果我们想扩展 DrawAllShapes
函数的行为来绘制一个新的图形种类,我们所需要做的就是增加一个从 Shape
类衍生的子类。而DrawAllShapes 函数则无需进行修改。因此DrawAllShapes
符合了开放封闭原则,它的行为可以不通过对其修改而扩展。
在比较现实的情况中Shape
类可能包含很多个方法。但是在应用程序中增加一个新的图形仍然是非常简单的,因为所需要做的仅是创建一个衍生类来实现这些函数。同时,我们也不再需要在应用程序内查找所有需要修改的位置了。
因为更改符合开放封闭原则的程序是通过增加新的代码,而不是修改已存在的代码,之前描述的那种级联式的更改也就不存在了。
**策略性的闭合Strategic Closure**
要明白程序是不可能 100% 完全封闭的。例如,试想上面的 Shape
示例,如果我们现在决定所有的 Circle 都应该在 Square 之前先进行绘制,则
DrawAllShapes 函数将会发生什么呢DrawAllShapes
函数是不可能对这样的变化保持封闭的。通常来说,无论模块的设计有多封闭,总是有各种各样的变化会打破这种封闭。
因此,完全闭合是不现实的,所以必须讲究策略。也就是说,**程序设计师必须甄别其设计对哪些变化封闭**。这需要一些基于经验的预测。有经验的设计师会很好的了解用户和所在的行业,以判断各种变化的可能性。然后可以确定对最有可能的变化保持开放封闭原则。
**使用抽象来获取显示地闭合**
那我们该如何使 DrawAllShapes
函数对绘制逻辑中的排序的变化保持闭合呢?要记住闭合是基于抽象的。因此,为了使
DrawAllShapes
对排序闭合,则我们需要对排序进行某种程度的抽象。上述例子中关于排序的一个特例就是某种类别的图形需要在其他类别的图像之前进行绘制。
一个排序策略就是,给定任意两个对象,可以发现哪一个应当被先绘制。因此,我们可以在
Shape 中定义一个名为 Precedes 的方法,它可以接受另一个 Shape 作为参数并返回一个
bool 类型的结果。如果结果为 true 则表示接收调用的 Shape 对象应排在被作为参数的
Shape 对象的前面。
我们可以使用重载操作符技术来实现这样的比较功能。这样通过比较我们就可以得到两个
Shape 对象的相对顺序,然后排序后就可以按照顺序进行绘制。
下面显示了简单实现的代码。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 public abstract class Shape 2 { 3 public abstract void Draw(); 4 5 public bool
Precedes(Shape another) 6 { 7 if (another is Circle) 8 return true; 9 else10
return false;11 } 12 } 13 14 public class Circle : Shape 15 { 16 public override
void Draw() 17 { 18 // draw circle on GUI19 } 20 } 21 22 public class Square :
Shape 23 { 24 public override void Draw() 25 { 26 // draw square on GUI27 } 28 }
29 30 public class ShapeComparer : IComparer\<Shape\> 31 { 32 public int
Compare(Shape x, Shape y) 33 { 34 return x.Precedes(y) ? 1 : 0;35 } 36 } 37 38
public class Client 39 { 40 public void DrawAllShapes(List\<Shape\> shapes) 41 {
42 SortedSet\<Shape\> orderedList = 43 new SortedSet\<Shape\>(shapes, new
ShapeComparer()); 44 45 foreach (var shape in orderedList) 46 { 47 shape.Draw();
48 } 49 } 50 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
这达成了排序 Shape
对象的目的,并可按照适当的顺序进行排序。但我们仍然还没有一个合适的排序抽象。以现在这种情况,单独的
Shape 对象将不得不覆写 Precedes 方法来指定顺序。这将如何工作呢?我们需要在
Precedes 中写什么样的代码才能确保 Circle 能够在 Square 之前绘制呢?
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 public bool Precedes(Shape another) 2 { 3 if (another is Circle) 4 return
true;5 else6 return false;7 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
可以看出,这个函数不符合开放封闭原则。无法使其对新衍生出的 Shape
子类保持封闭。每次当一个新的 Shape 衍生类被创建时,这个方法将总是被修改。
**使用 "数据驱动Data Driven" 的方法来达成闭合**
使用表驱动Table Driven方法能够达成对 Shape
衍生类的闭合,而不会强制修改每个衍生类。
下面展示了一种可能的设计。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 private Dictionary\<Type, int\> \_typeOrderTable = new Dictionary\<Type,
int\>(); 2 3 private void Initialize() 4 { 5
\_typeOrderTable.Add(typeof(Circle), 2); 6 \_typeOrderTable.Add(typeof(Square),
1); 7 } 8 9 public bool Precedes(Shape another) 10 { 11 return
\_typeOrderTable[this.GetType()] \> \_typeOrderTable[another.GetType()];12 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
通过使用这种方法我们已经成功地使 DrawAllShapes
函数在一般情况下对排序问题保持封闭,并且每个 Shape 的衍生类都对新的 Shape
子类或者排序策略的修改(例如修改排序规则以使先绘制 Square等保持封闭。
这里仍然无法对多种 Shape
的顺序保持封闭的就是表Table本身。但我们可以将这个表定义放置在单独的模块中使表与其他模块隔离这样对表的更改则不再会对任何其他模块产生影响。
**进一步的扩展闭合**
这并不是故事的尾声。
我们可以掌控 Shape 的层级结构和 DrawAllShapes 函数对依据不同 Shape
类型的排序规则的闭合。尽管如此Shape
的衍生类对不判断图形类型的排序规则是非闭合的。看起来可能我们希望可以根据更高级别的结构来对
Shape
进行排序。对这个问题的一个完整的研究已经超出了这篇文章的范围,但是感兴趣的读者可以考虑如何实现。例如让一个
OrderedShape 类来持有一个抽象的 OrderedObject 类,而其自身同时继承自 Shape 和
OrderedObject 类的实现。
**总结**
关于开放封闭原则Open Closed
Principle还有很多可以讲的。在很多方面这个原则都是面向对象设计的核心。始终遵循该原则才能从面向对象技术中持续地获得最大的益处例如可重用性和可维护性。同时对该原则的遵循也不是通过使用一种面向对象的编程语言就能够达成的。更确切的说它需要程序设计师更专注于将抽象技术应用到程序中那些趋于变化的部分上。
**面向对象设计的原则**
| SRP | [单一职责原则](http://www.cnblogs.com/gaochundong/p/single_responsibility_principle.html) | [Single Responsibility Principle](http://www.cnblogs.com/gaochundong/p/single_responsibility_principle.html) |
|------|--------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------|
| OCP | [开放封闭原则](http://www.cnblogs.com/gaochundong/p/open_closed_principle.html) | [Open Closed Principle](http://www.cnblogs.com/gaochundong/p/open_closed_principle.html) |
| LSP | [里氏替换原则](http://www.cnblogs.com/gaochundong/p/liskov_substitution_principle.html) | Liskov Substitution Principle |
| ISP | [接口分离原则](http://www.cnblogs.com/gaochundong/p/interface_segregation_principle.html) | [Interface Segregation Principle](http://www.cnblogs.com/gaochundong/p/interface_segregation_principle.html) |
| DIP | [依赖倒置原则](http://www.cnblogs.com/gaochundong/p/dependency_inversion_principle.html) | [Dependency Inversion Principle](http://www.cnblogs.com/gaochundong/p/dependency_inversion_principle.html) |
| LKP | [最少知识原则](http://www.cnblogs.com/gaochundong/p/least_knowledge_principle.html) | [Least Knowledge Principle](http://www.cnblogs.com/gaochundong/p/least_knowledge_principle.html) |
**参考资料**
- *OCPThe Open-Closed Principle by Robert C. Martin “Uncle Bob”*
- *The SOLID Principles, Explained with Motivational Posters*
- *Dangers of Violating SOLID Principles in C\#*
- *An introduction to the SOLID principles of OO design*
- *10 Golden Rules Of Good OOP*
- *10 Object Oriented Design principles Java programmer should know*

View File

@@ -0,0 +1,134 @@
**最少知识原则Least Knowledge Principle或者称迪米特法则Law of
Demeter是一种面向对象程序设计的指导原则它描述了一种保持代码松耦合的策略。**其可简单的归纳为:
> Each unit should have only limited knowledge about other units: only units
> "closely" related to the current unit.
> 每个单元对其他单元只拥有有限的知识,只了解与当前单元紧密联系的单元;
再简洁些:
> Each unit should only talk to its friends; don't talk to strangers.
> 每个单元只能和它的 "朋友" 交谈,不能和 "陌生人" 交谈;
更简洁些:
> **Only talk to your immediate friends.**
> 只和自己直接的 "朋友" 交谈。
应用到面向对象的程序设计中时,可描述为
"类应该与其协作类进行交互但无需了解它们的内部结构"。
> A class should interact directly with its collaborators and be shielded from
> understanding their internal structure.
迪米特法则Law of Demeter由 [Northeastern
University](http://www.northeastern.edu/) 的 [Ian
Holland](http://www.ccs.neu.edu/research/demeter/holland/) 在 1987 年提出,"Law
of Demeter" 名称是来自当时正在进行的一项研究 "[The Demeter
Project](http://www.ccs.neu.edu/research/demeter/)"。
> Demeter = Greek Goddess of Agriculture; grow software in small steps.
在 2004 年Karl Lieberherr 在其论文 "[Controlling the Complexity of Software
Designs](http://www.ccs.neu.edu/research/demeter/papers/icse-04-keynote/ICSE2004.pdf)"
中将 LoD 的定义由 "Only talk to your friends" 改进为:
> **Only talk to your friends who share your concerns.**
改进后的原则称为 LoDCLaw of Demeter for
Concerns它为软件设计带来了两个主要的益处
> It leads to better information hiding.
> It leads to less information overload.
即,**更好的信息隐藏和更少的信息重载**。LoDC
原则在面向方面的软件开发AOSDAspect-Oriented Software
Development中有着良好的应用。
**最少知识原则在面向对象编程中的应用**
在 "Law of Demeter" 应用于面向对象编程中时,可以简称为 "LoD-FLaw of Demeter
for Functions/Methods"。
对于对象 O 中的一个方法 m m 方法仅能访问如下这些类型的对象:
1. O 对象自身;
2. m 方法的参数对象;
3. 任何在 m 方法内创建的对象;
4. O 对象直接依赖的对象;
具体点儿就是,对象应尽可能地避免调用由另一个方法返回的对象的方法。
现代面向对象程序设计语言通常使用 "." 作为访问标识LoD 可以被简化为
"仅使用一个点use only one dot"。也就是说,代码 a.b.Method() 违反了 LoD
a.Method() 则符合
LoD。打个比方人可以命令一条狗行走但是不应该直接指挥狗的腿行走应该由狗去指挥它的腿行走。
你是否见过类似下面这样儿的代码?
1 public Emailer(Server server) {…} // taking a server in the constructor2
public void sendSupportEmail(String message, String toAddress) { 3 EmailSystem
emailSystem = server.getEmailSystem(); 4 String fromAddress =
emailSystem.getFromAddress(); 5
emailSystem.getSenderSubsystem().send(fromAddress, toAddress, message); 6 }
上面这个设计有几点问题:
1. 复杂而且看起来不必要。Emailer 与多个它可能不是真的需要的 API 进行交互,例如
EmailSystem。
2. 依赖于 Server 和 EmailSystem 的内部结构,如果两者之一进行了修改,则 Emailer
有可能被破坏。
3. 不能重用。任何其他的 Server 实现也必须包含一个能返回 EmailSystem 的 API。
除了上面这几个问题之外还有一个问题是这段代码是可测试的吗你可能会说肯定可测啊因为这个类使用了依赖注入Dependency
Injection我们可以模拟 Server、EmailSystem 和 Sender
类。但真正的问题是,除了多出了这些模拟代码,任何对 API
的修改都将破坏所有的测试用例,使得设计和测试都变得非常脆弱。
解决上述问题的办法就是应用最少知识原则仅通过构造函数注入直接依赖的对象。Emailer
无需了解是否 Server 类包含了一个 EmailSystem也不知道 EmailSystem 包含了一个
Sender。
1 public Emailer(Sender sender, String fromAddress) {…} 2 public void
sendSupportEmail(String message, String toAddress) { 3 sender.send(fromAddress,
toAddress, message); 4 }
这个设计较为合理些。现在 Emailer 不再依赖 Server 和
EmailSystem而是通过构造函数得到了所有的依赖项。同时 Emailer
也变得更容易理解,因为所有与其交互的对象都显式的呈现出来。
Emailer 与 Server 和 EmailSystem 也达到了解耦合的效果。Emailer 不再需要了解
Server 和 EmailSystem 的内部结构,任何对 Server 和 EmailSystem
的修改都不再会影响 Emailer。
而且Emailer 的变得更易被复用。如果切换到另外一个环境中时,仅需实现一个不同的
Sender 即可。
对于测试而言,现在我们仅需模拟 Sender 依赖即可。
**应用最少知识原则优点和缺点**
优点:遵守 Law of Demeter 将降低模块间的耦合,提升了软件的可维护性和可重用性。
缺点:应用 Law of Demeter
可能会导致不得不在类中设计出很多用于中转的包装方法Wrapper
Method这会提升类设计的复杂度。
**面向对象设计的原则**
| SRP | [单一职责原则](http://www.cnblogs.com/gaochundong/p/single_responsibility_principle.html) | [Single Responsibility Principle](http://www.cnblogs.com/gaochundong/p/single_responsibility_principle.html) |
|------|--------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------|
| OCP | [开放封闭原则](http://www.cnblogs.com/gaochundong/p/open_closed_principle.html) | [Open Closed Principle](http://www.cnblogs.com/gaochundong/p/open_closed_principle.html) |
| LSP | [里氏替换原则](http://www.cnblogs.com/gaochundong/p/liskov_substitution_principle.html) | Liskov Substitution Principle |
| ISP | [接口分离原则](http://www.cnblogs.com/gaochundong/p/interface_segregation_principle.html) | [Interface Segregation Principle](http://www.cnblogs.com/gaochundong/p/interface_segregation_principle.html) |
| DIP | [依赖倒置原则](http://www.cnblogs.com/gaochundong/p/dependency_inversion_principle.html) | [Dependency Inversion Principle](http://www.cnblogs.com/gaochundong/p/dependency_inversion_principle.html) |
| LKP | [最少知识原则](http://www.cnblogs.com/gaochundong/p/least_knowledge_principle.html) | [Least Knowledge Principle](http://www.cnblogs.com/gaochundong/p/least_knowledge_principle.html) |

View File

@@ -0,0 +1,268 @@
设计模式之美Abstract Factory抽象工厂
**索引**
- 别名
- 意图
- 结构
- 参与者
- 适用性
- 缺点
- 效果
- 相关模式
- 命名约定
- 实现
- 实现方式(一):使用 Factory Method 来实现 Abstract Factory。
- 实现方式(二):使用 Prototype 来实现 Abstract Factory。
- 实现方式(三):定义可扩展的 Abstract Factory。
- 实现方式(四):使用模板以避免创建子类。
**别名**
- Kit
**意图**
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
Provide an interface for creating families of related or dependent objects
without specifying their concrete classes.
**结构**
![126081935533.png](media/abc2df6bb6578e6901711922fb704575.png)
**参与者**
AbstractFactory
- 声明一个创建抽象产品对象的操作接口。
ConcreteFactory
- 实现创建具体产品对象的操作。
AbstractProduct
- 为一类产品对象声明一个接口。
ConcreteProduct
- 定义一个将被相应的具体工厂创建的产品对象。
- 实现 AbstractProduct 接口。
Client
- 仅适用由 AbstractFactory 和 AbstractProduct 类声明的接口。
**适用性**
在以下情况下可以使用 Abstract Factory 模式:
- 一个系统要独立于它的产品的创建、组合和表示时。
- 一个系统要由多个产品系列中的一个来配置时。
- 当你要强调一系列相关的产品对象的设计以便进行联合使用时。
- 当你提供一个产品类库,而只想显示它们的接口而不是实现时。
**缺点**
- 难以支持新种类的产品。支持新种类的产品就需要扩展 AbstractFactory
接口,这将引起所有子类的改变。
**效果**
- 它分离了具体的类。
- 它使得易于交换产品系列。
- 它有利于产品的一致性。
**相关模式**
- Abstract Factory 经常用 [Factory
Method](http://www.cnblogs.com/gaochundong/p/design_pattern_factory_method.html)
来实现。
- Abstract Factory 也可以用
[Prototype ](http://www.cnblogs.com/gaochundong/p/design_pattern_prototype.html)来实现。
- 一个具体的工厂可以是一个
[Singleton](http://www.cnblogs.com/gaochundong/p/design_pattern_singleton.html)。
**命名约定**
使用命名约定是一个好习惯,例如,总是声明那些定义为抽象工厂的类为 XxxKit。
**实现**
**实现方式(一):使用 Factory Method 来实现 Abstract Factory。**
一个具体的工厂将为每个产品重定义该工厂方法以指定产品。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace AbstractFactoryPattern.Implementation1 2 { 3 public abstract class
AbstractOrInterfaceOfFactoryKit 4 { 5 public abstract
AbstractOrInterfaceOfProductA CreateProductA(); 6 public abstract
AbstractOrInterfaceOfProductB CreateProductB(); 7 } 8 9 public abstract class
AbstractOrInterfaceOfProductA 10 { 11 } 12 13 public abstract class
AbstractOrInterfaceOfProductB 14 { 15 } 16 17 public class ConcreteFactoryKit1 :
AbstractOrInterfaceOfFactoryKit 18 { 19 public override
AbstractOrInterfaceOfProductA CreateProductA() 20 { 21 return new
ConcreteProductA(); 22 } 23 24 public override AbstractOrInterfaceOfProductB
CreateProductB() 25 { 26 return new ConcreteProductB(); 27 } 28 } 29 30 public
class ConcreteProductA : AbstractOrInterfaceOfProductA 31 { 32 } 33 34 public
class ConcreteProductB : AbstractOrInterfaceOfProductB 35 { 36 } 37 38 public
class Client 39 { 40 public void TestCase1() 41 { 42
AbstractOrInterfaceOfFactoryKit kit = new ConcreteFactoryKit1(); 43
AbstractOrInterfaceOfProductA productA = kit.CreateProductA(); 44
AbstractOrInterfaceOfProductB productB = kit.CreateProductB(); 45 } 46 } 47 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
**实现方式(二):使用 Prototype 来实现 Abstract Factory。**
具体工厂使用产品系列中每一个产品的原型实例来初始化,且它通过复制它的原型来创建新的产品。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace AbstractFactoryPattern.Implementation2 2 { 3 public abstract class
AbstractOrInterfaceOfFactoryKit 4 { 5 public abstract
AbstractOrInterfaceOfProductA CreateProductA(); 6 public abstract
AbstractOrInterfaceOfProductB CreateProductB(); 7 } 8 9 public abstract class
AbstractOrInterfaceOfProductA 10 { 11 public abstract
AbstractOrInterfaceOfProductA Clone(); 12 } 13 14 public abstract class
AbstractOrInterfaceOfProductB 15 { 16 public abstract
AbstractOrInterfaceOfProductB Clone(); 17 } 18 19 public class
ConcreteFactoryKit1 : AbstractOrInterfaceOfFactoryKit 20 { 21 public override
AbstractOrInterfaceOfProductA CreateProductA() 22 { 23 return new
ConcreteProductA(); 24 } 25 26 public override AbstractOrInterfaceOfProductB
CreateProductB() 27 { 28 return new ConcreteProductB(); 29 } 30 } 31 32 public
class ConcreteFactoryKit2 : AbstractOrInterfaceOfFactoryKit 33 { 34 private
AbstractOrInterfaceOfProductA \_prototypeOfProductA; 35 private
AbstractOrInterfaceOfProductB \_prototypeOfProductB; 36 37 public
ConcreteFactoryKit2( 38 AbstractOrInterfaceOfProductA prototypeOfProductA, 39
AbstractOrInterfaceOfProductB prototypeOfProductB) 40 { 41 \_prototypeOfProductA
= prototypeOfProductA; 42 \_prototypeOfProductB = prototypeOfProductB; 43 } 44
45 public override AbstractOrInterfaceOfProductA CreateProductA() 46 { 47 return
\_prototypeOfProductA.Clone(); 48 } 49 50 public override
AbstractOrInterfaceOfProductB CreateProductB() 51 { 52 return
\_prototypeOfProductB.Clone(); 53 } 54 } 55 56 public class ConcreteProductA :
AbstractOrInterfaceOfProductA 57 { 58 public override
AbstractOrInterfaceOfProductA Clone() 59 { 60 return new ConcreteProductA(); 61
} 62 } 63 64 public class ConcreteProductB : AbstractOrInterfaceOfProductB 65 {
66 public override AbstractOrInterfaceOfProductB Clone() 67 { 68 return new
ConcreteProductB(); 69 } 70 } 71 72 public class Client 73 { 74 public void
TestCase2() 75 { 76 AbstractOrInterfaceOfFactoryKit kit1 = new
ConcreteFactoryKit1(); 77 AbstractOrInterfaceOfProductA productA1 =
kit1.CreateProductA(); 78 AbstractOrInterfaceOfProductB productB1 =
kit1.CreateProductB(); 79 80 AbstractOrInterfaceOfFactoryKit kit2 = new
ConcreteFactoryKit2(productA1, productB1); 81 AbstractOrInterfaceOfProductA
productA2 = kit2.CreateProductA(); 82 AbstractOrInterfaceOfProductB productB2 =
kit2.CreateProductB(); 83 } 84 } 85 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
**实现方式(三):定义可扩展的 Abstract Factory。**
Abstract Factory
通常为每一种它可以生产的产品定义一个操作。产品的种类被编码在操作型构中。
增加一种新的产品要求改变 Abstract Factory 的接口以及所有与它相关的类。
一个更灵活但不太安全的设计是给创建对象的操作增加一个参数。该参数指定了将被创建的对象的种类。
该参数可以是一个类标识符、一个整数、一个字符串,或其他任何可以标识这种产品的东西。
这样改动之后Abstract Factory 只需要一个 "Make"
操作和一个指示要创建对象的种类的参数。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace AbstractFactoryPattern.Implementation3 2 { 3 public enum
ProductCategory 4 { 5 ProductA, 6 ProductB, 7 } 8 9 public abstract class
AbstractOrInterfaceOfFactoryKit 10 { 11 public abstract object
CreateProduct(ProductCategory category); 12 } 13 14 public abstract class
AbstractOrInterfaceOfProductA 15 { 16 } 17 18 public abstract class
AbstractOrInterfaceOfProductB 19 { 20 } 21 22 public class ConcreteFactoryKit1 :
AbstractOrInterfaceOfFactoryKit 23 { 24 public override object
CreateProduct(ProductCategory category) 25 { 26 switch (category) 27 { 28 case
ProductCategory.ProductA: 29 return new ConcreteProductA(); 30 case
ProductCategory.ProductB: 31 return new ConcreteProductB(); 32 default:33 throw
new NotSupportedException(); 34 } 35 } 36 } 37 38 public class ConcreteProductA
: AbstractOrInterfaceOfProductA 39 { 40 } 41 42 public class ConcreteProductB :
AbstractOrInterfaceOfProductB 43 { 44 } 45 46 public class Client 47 { 48 public
void TestCase3() 49 { 50 AbstractOrInterfaceOfFactoryKit kit = new
ConcreteFactoryKit1(); 51 AbstractOrInterfaceOfProductA productA =
(AbstractOrInterfaceOfProductA)kit.CreateProduct(ProductCategory.ProductA); 52
AbstractOrInterfaceOfProductB productB =
(AbstractOrInterfaceOfProductB)kit.CreateProduct(ProductCategory.ProductB); 53 }
54 } 55 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
**实现方式(四):使用模板以避免创建子类。**
使用C\#中的泛型实现抽象工厂。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace AbstractFactoryPattern.Implementation4 2 { 3 public abstract class
AbstractOrInterfaceOfFactoryKit 4 { 5 public abstract
AbstractOrInterfaceOfProductA CreateProductA(); 6 public abstract
AbstractOrInterfaceOfProductB CreateProductB(); 7 public abstract
AbstractOrInterfaceOfProductC CreateProductC\<TC\>() 8 where TC :
AbstractOrInterfaceOfProductC, new(); 9 } 10 11 public abstract class
AbstractOrInterfaceOfProductA 12 { 13 } 14 15 public abstract class
AbstractOrInterfaceOfProductB 16 { 17 } 18 19 public abstract class
AbstractOrInterfaceOfProductC 20 { 21 } 22 23 public class
ConcreteFactoryKit1\<TA, TB\> : AbstractOrInterfaceOfFactoryKit 24 where TA :
AbstractOrInterfaceOfProductA, new()25 where TB : AbstractOrInterfaceOfProductB,
new()26 { 27 public override AbstractOrInterfaceOfProductA CreateProductA() 28 {
29 return new TA(); 30 } 31 32 public override AbstractOrInterfaceOfProductB
CreateProductB() 33 { 34 return new TB(); 35 } 36 37 public override
AbstractOrInterfaceOfProductC CreateProductC\<TC\>() 38 { 39 return new TC(); 40
} 41 } 42 43 public class ConcreteProductA : AbstractOrInterfaceOfProductA 44 {
45 } 46 47 public class ConcreteProductB : AbstractOrInterfaceOfProductB 48 { 49
} 50 51 public class ConcreteProductC : AbstractOrInterfaceOfProductC 52 { 53 }
54 55 public class Client 56 { 57 public void TestCase4() 58 { 59
AbstractOrInterfaceOfFactoryKit kit = new ConcreteFactoryKit1\<ConcreteProductA,
ConcreteProductB\>(); 60 AbstractOrInterfaceOfProductA productA =
kit.CreateProductA(); 61 AbstractOrInterfaceOfProductB productB =
kit.CreateProductB(); 62 AbstractOrInterfaceOfProductC productC =
kit.CreateProductC\<ConcreteProductC\>(); 63 } 64 } 65 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码

View File

@@ -0,0 +1,141 @@
设计模式之美Adapter适配器
**索引**
- 别名
- 意图
- 结构
- 参与者
- 适用性
- 效果
- 相关模式
- 实现
- 实现方式(一):简单直接的对象适配器。
- 实现方式(二):实现双向类适配器。
**别名**
- 包装器Wrapper
**意图**
将一个类的接口转换成客户希望的另外一个接口。
Adapter 模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
Convert the interface of a class into another interface clients expect.
Adapter lets classes work together that couldn't otherwise because of
incompatible interfaces.
**结构**
类适配器使用多重继承对一个接口与另一个接口进行匹配。
![005586158107.png](media/06f6afee0fb26038e1df96cc245c24f4.png)
对象适配器依赖于对象组合。
![006051933731.png](media/9bae3ff976c8470b1d6ae9e9768409e4.png)
**参与者**
Target
- 定义 Client 使用的与特定领域相关的接口。
Client
- 与符合 Target 接口的对象协同。
Adaptee
- 定义一个已经存在的接口,这个接口需要适配。
Adapter
- 对 Adaptee 的接口与 Target 接口进行适配。
**适用性**
在以下情况下可以使用 Adapter 模式:
- 你想使用一个已经存在的类,而它的接口不符合你的需求。
- 你想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类协同工作。
- 你想使用一些已经存在的类,但是不可能对每一个都进行子类化匹配它们的接口。对象适配器可以适配它的父类的接口。
**效果**
- 允许一个 Adapter 与多个 Adaptee Adaptee 本身及它的子类同时协同。Adapter
也可以一次给所有的 Adaptee 添加功能。
- 使得重定义 Adaptee 的行为比较困难。这就需要生成 Adaptee 的子类并且使得
Adapter 引用这个子类而不是引用 Adaptee 自身。
**相关模式**
- Bridge 模式的结构与对象 Adapter 模式类似,但是 Bridge
模式的出发点不同Bridge
目的是将接口部分和实现部分分离,从而对它们可以较为容易也相对独立的加以改变。而
Adapter 则意味着改变一个已有对象的接口。
- Decorator 模式增强了其他对象的功能而同时又不改变它的接口。因此 Decorator
对应用程序的透明性比 Adapter 要好。结果是 Decorator 支持递归组合,而 Adapter
无法实现这一点。
- Proxy 模式在不改变它的接口的条件下,为另一个对象定义了一个代理。
**实现**
**实现方式(一):简单直接的对象适配器。**
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace AdapterPattern.Implementation1 2 { 3 public class ParentAdaptee 4 {
5 public virtual string SpecificRequest() 6 { 7 return "ParentAdaptee"; 8 } 9 }
10 11 public class ChildAdaptee : ParentAdaptee 12 { 13 public override string
SpecificRequest() 14 { 15 return "ChildAdaptee";16 } 17 } 18 19 public class
Target 20 { 21 public virtual string Request() 22 { 23 return "Target";24 } 25 }
26 27 public class Adapter : Target 28 { 29 private ParentAdaptee \_adaptee; 30
31 public Adapter(ParentAdaptee adaptee) 32 { 33 \_adaptee = adaptee; 34 } 35 36
public override string Request() 37 { 38 return \_adaptee.SpecificRequest(); 39
} 40 } 41 42 public class Client 43 { 44 public void TestCase1() 45 { 46
ParentAdaptee adaptee = new ChildAdaptee(); 47 Target target = new
Adapter(adaptee); 48 var result = target.Request(); 49 } 50 } 51 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
**实现方式(二):实现双向类适配器。**
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace AdapterPattern.Implementation2 2 { 3 public class ParentAdaptee 4 {
5 public virtual string SpecificRequest() 6 { 7 return "ParentAdaptee"; 8 } 9 }
10 11 public class ChildAdaptee : ParentAdaptee 12 { 13 public override string
SpecificRequest() 14 { 15 return "ChildAdaptee";16 } 17 } 18 19 public interface
ITarget 20 { 21 string Request(); 22 } 23 24 public class Adapter :
ChildAdaptee, ITarget 25 { 26 public Adapter() 27 { 28 } 29 30 public string
Request() 31 { 32 return base.SpecificRequest();33 } 34 } 35 36 public class
Client 37 { 38 public void TestCase2() 39 { 40 ITarget target = new Adapter();
41 var result = target.Request(); 42 } 43 } 44 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码

View File

@@ -0,0 +1,140 @@
行为型模式涉及到算法和对象间职责的分配。
行为模式不仅描述对象或类的模式,还描述它们之间的通信模式。
这些模式刻划了在运行时难以跟踪的复杂的控制流。它们将你的注意力从控制流转移到对象间的联系方式上来。
- 行为类模式使用继承机制在类间分派行为。
- 行为对象模式使用对象复合而不是继承。描述一组对等的对象怎样相互协作以完成任一个对象都无法完成的任务。
**行为型模式**
- **Chain of Responsibility职责链**
- 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。
- 将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它位置。
- Avoid coupling the sender of a request to its receiver by giving more
than one object a chance to handle the request.
- Chain the receiving objects and pass the request along the chain until
an object handles it.
- **Command命令**
- 将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
- Encapsulate a request as an object, thereby letting you parameterize
clients with different requests, queue or log requests, and support
undoable operations.
- **Interpreter解释器**
- 给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
- Given a language, define a represention for its grammar along with an
interpreter that uses the representation to interpret sentences in the
language.
- **Iterator迭代器**
- 提供一种方法顺序访问一个聚合对象中各个元素,而又不需暴露该对象的内部表示。
- Provide a way to access the elements of an aggregate object sequentially
without exposing its underlying representation.
- **Observer观察者**
- 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
- Define a one-to-many dependency between objects so that when one object
changes state, all its dependents are notified and updated
automatically.
- **Mediator中介者**
- 用一个中介对象来封装一系列的对象交互。
- 中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
- Define an object that encapsulates how a set of objects interact.
- Mediator promotes loose coupling by keeping objects from referring to
each other explicitly, and it lets you vary their interaction
independently.
- **Memento备忘录**
- 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
- Without violating encapsulation, capture and externalize an object's
internal state so that the object can be restored to this state later.
- **State状态**
- 允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。
- Allow an object to alter its behavior when its internal state changes.
The object will appear to change its class.
- **Strategy策略**
- 定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。使得算法可独立于使用它的客户而变化。
- Define a family of algorithms, encapsulate each one, and make them
interchangeable.
- Strategy lets the algorithm vary independently from clients that use it.
- **Template Method模板方法**
- 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。
- Template Method
使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
- Define the skeleton of an algorithm in an operation, deferring some
steps to subclasses.
- Template Method lets subclasses redefine certain steps of an algorithm
without changing the algorithm's structure.
- **Visitor访问者**
- 表示一个作用于某对象结构中的各元素的操作。
- Visitor 使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
- Represent an operation to be performed on the elements of an object
structure.
- Visitor lets you define a new operation without changing the classes of
the elements on which it operates.
**封装变化**
当一个程序的某些方面的特征经常发生改变时,行为型模式就定义一个封装这个方面的对象。
这样当该程序的其他部分依赖于这个方面时,它们都可以与此对象协作。
这些模式通常定义一个抽象类来描述这些封装变化的对象,并且通常该模式依赖这个对象来命名。
- 一个 Strategy 对象封装一个 Algorithm。
- 一个 State 对象封装一个与状态相关的行为。
- 一个 Mediator 对象封装对象间的协议。
- 一个 Iterator 对象封装访问和遍历一个聚合对象中的各个构件的方法。
大多数模式有两种对象:封装该方面特征的新对象,和使用这些新的对象的已有对象。
**对象作为参数**
一些模式引入总是被用作参数的对象。例如一个 Visitor 对象是一个多态的 Accept
操作的参数。
一些模式定义一些可作为令牌到处传递的对象。例如 Command 代表一个请求Memento
代表一个对象在某个时刻的内部状态。

View File

@@ -0,0 +1,135 @@
设计模式之美Bridge桥接
**索引**
- 别名
- 意图
- 结构
- 参与者
- 适用性
- 效果
- 相关模式
- 实现
- 实现方式(一):使用 Bridge 模式分离抽象部分和实现部分。
**别名**
- Handle
- Body
**意图**
将抽象部分与它的实现部分分离,使它们都可以独立地变化。
Decouple an abstraction from its implementation so that the two can vary
independently.
**结构**
![242197409687.png](media/ee4a265f47725eacf53741680d262704.png)
**参与者**
Abstraction
- 定义抽象类的接口。
- 维护一个指向 Implementor 类型对象的指针。
RefinedAbstraction
- 扩充由 Abstraction 定义的接口。
Implementor
- 定义实现类的接口,该接口不一定要与 Abstraction
接口完全一致,甚至可以完全不同。
- Implementor 接口仅提供基本操作Abstraction
则定义了基于这些基本操作的较高层次的操作。
ConcreteImplementor
- 实现 Implementor 接口并定义它的具体实现。
**适用性**
在以下情况下可以使用 Bridge 模式:
- 你不希望在抽象和它的实现部分之间有一个固定的绑定关系。比如需要在程序运行时刻实现部分应可以被选择或者切换。
- 类的抽象以及它的实现都应该可以通过生成子类的方法加以扩充。
- 对一个抽象的实现部分的修改应对客户不产生影响,即客户的代码不必重新编译。
- 你想对客户完全隐藏抽象的实现部分。
- 类的层次需要将一个对象分解成两个部分。
- 你想在多个对象间共享实现,但同时要求客户并不知道这一点。
**效果**
- 分离接口及其实现部分。
- 提高可扩充性。
- 实现细节对客户透明。
**相关模式**
- Abstract Factory 模式可以用来创建和配置一个特定的 Bridge 模式。
- Adaptor
模式用来帮助无关的类协同工作它通常在系统设计完成后才会被使用。Bridge
模式则是在系统开始时就被使用,它使得抽象接口和实现部分可以独立进行改变。
- Bridge 模式的结构与对象 Adapter 模式类似,但是 Bridge
模式的出发点不同Bridge
目的是将接口部分和实现部分分离,从而对它们可以较为容易也相对独立的加以改变。而
Adapter 则意味着改变一个已有对象的接口。
**实现**
**实现方式(一):使用 Bridge 模式分离抽象部分和实现部分。**
当一个抽象可能有多个实现时,通常用继承来协调它们。抽象类定义对该抽象的接口,而具体的子类则用不同方式加以实现。
但是此方法有时不够灵活。继承机制将抽象部分与它的实现部分固定在一起,使得难以对抽象部分和实现部分独立地进行修改、扩充和重用。
使用 Bridge 模式,它在抽象类与它的实现直接起到了桥梁作用,使它们可以独立地变化。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace BridgePattern.Implementation1 2 { 3 public class Abstraction 4 { 5
protected IImplementor \_implementor; 6 7 public Abstraction(IImplementor
implementor) 8 { 9 \_implementor = implementor; 10 } 11 12 public virtual void
Operation() 13 { 14 \_implementor.OperationImp1(); 15 } 16 } 17 18 public
interface IImplementor 19 { 20 void OperationImp1(); 21 } 22 23 public class
ConcreteImplementorA : IImplementor 24 { 25 public void OperationImp1() 26 { 27
// do something28 } 29 } 30 31 public class ConcreteImplementorB : IImplementor
32 { 33 public void OperationImp1() 34 { 35 // do something36 } 37 } 38 39
public class ChildAbstraction : Abstraction 40 { 41 public
ChildAbstraction(IImplementor implementor) 42 : base(implementor)43 { 44 } 45 46
public override void Operation() 47 { 48 base.Operation();49 // do some others50
} 51 } 52 53 public class Client 54 { 55 public void TestCase1() 56 { 57
IImplementor implementor1 = new ConcreteImplementorA(); 58 IImplementor
implementor2 = new ConcreteImplementorB(); 59 60 Abstraction abstraction1 = new
Abstraction(implementor1); 61 Abstraction abstraction2 = new
ChildAbstraction(implementor2); 62 63 abstraction1.Operation(); 64
abstraction2.Operation(); 65 } 66 } 67 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码

View File

@@ -0,0 +1,199 @@
设计模式之美Builder生成器
**索引**
- 意图
- 结构
- 参与者
- 适用性
- 效果
- 相关模式
- 实现
- 实现方式Builder 为每个构件定义一个操作。
- 实现方式Builder 将构件返回给 DirectorDirector 将构件传递给
Builder 中的下一个步骤。
**意图**
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
Separate the construction of a complex object from its representation so that
the same construction process can create different representations.
**结构**
![215216159109.png](media/ebed27694b48db7da45d4f8a573d1001.png)
**参与者**
Builder
- 为创建一个 Product 对象的各个部件指定抽象接口。
ConcreteBuilder
- 实现 Builder 的接口以构造和装配该产品的各个部件。
- 定义并明确它所创建的表示。
- 提供一个检索产品的接口。
Director
- 构造一个使用 Builder 接口的对象。
Product
- 表示被构造的复杂对象。ConcreteBuilder
创建该产品的内部表示并定义它的装配过程。
- 包含定义组成部件的类,包括将这些部件装配成最终产品的接口。
**适用性**
在以下情况下可以使用 Builder 模式:
- 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
- 当构造过程必须允许被构造的对象有不同的表示时。
**效果**
- 它使你可以改变一个产品的内部表示。在改变该产品的内部表示时所要做的只是定义一个新的
ConcreteBuilder。
- 它将构造代码和表示代码分开,提高了对象的模块性。客户不需要知道定义产品内部结构的类的所有信息。
- 它使你可以对构造过程进行更精细的控制。对象是在 Director
的控制下一步一步构造的,仅当产品构造完成时 Director 才从 Builder 中取回它。
**相关模式**
- Abstract Factory 和 Builder 相似,因为它也可以创建复杂对象。区别是 Builder
着重于一步步构造一个复杂对象。而 Abstract Factory
着重于多个系列的产品对象或简单或复杂。Builder
是在最后一步返回产品Abstract Factory 是立即返回。
- Composite 通常是用 Builder 生成的。
**实现**
![340175067017.png](media/e60068b39c1312cbb4ad787dc949202f.png)
**实现方式Builder 为每个构件定义一个操作。**
通常有一个抽象的 Builder 类为 Director 可能要求创建的每一个 "构件"
定义一个操作。这些操作默认情况下什么都不做。一个 ConcreteBuilder 类对它感兴趣的
"构件" 对应的操作进行重定义。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace BuilderPattern.Implementation1 2 { 3 public class ComplexProduct 4 {
5 public string ValueDependOnWeather { get; set; } 6 public string
ValueDependOnFortune { get; set; } 7 } 8 9 public abstract class
AbstractComplexProductBuilder 10 { 11 protected ComplexProduct \_complexProduct;
12 13 public void BeginBuild(ComplexProduct existingComplexProduct = null)14 {
15 if (existingComplexProduct == null)16 \_complexProduct = new
ComplexProduct(); 17 else18 \_complexProduct = existingComplexProduct; 19 } 20
21 public virtual void BuildValueDependOnWeatherPart(string weather) 22 { 23 //
could do nothing by default24 \_complexProduct.ValueDependOnWeather = weather;
25 } 26 27 public virtual void BuildValueDependOnFortunePart(string luck) 28 {
29 // could do nothing by default30 \_complexProduct.ValueDependOnFortune =
luck; 31 } 32 33 public ComplexProduct EndBuild() 34 { 35 return
this._complexProduct;36 } 37 } 38 39 public class ConcreteProductBuilderA :
AbstractComplexProductBuilder 40 { 41 private string \_dayOfWeek; 42 private int
\_luckyNumber; 43 44 public ConcreteProductBuilderA(string dayOfWeek, int
luckyNumber) 45 { 46 \_dayOfWeek = dayOfWeek; 47 \_luckyNumber = luckyNumber; 48
} 49 50 public override void BuildValueDependOnWeatherPart(string weather) 51 {
52 // something customized53 \_complexProduct.ValueDependOnWeather = \_dayOfWeek
\+ " is " + weather; 54 } 55 56 public override void
BuildValueDependOnFortunePart(string luck) 57 { 58 // something customized59 if
(\_luckyNumber == 8)60 \_complexProduct.ValueDependOnFortune = "Supper" + luck;
61 else62 \_complexProduct.ValueDependOnFortune = "Just so so" + luck; 63 } 64 }
65 66 public class GoodWeatherAndGoodLuckDirector 67 { 68 public void
ConstructWithGoodWeatherAndGoodLuck(AbstractComplexProductBuilder builder) 69 {
70 builder.BuildValueDependOnWeatherPart(@"PM2.5 \< 50");71
builder.BuildValueDependOnFortunePart(@"Good Luck");72 } 73 74 public void
ConstructWithBadWeatherAndBadLuck(AbstractComplexProductBuilder builder) 75 { 76
builder.BuildValueDependOnWeatherPart(@"PM2.5 \> 500");77
builder.BuildValueDependOnFortunePart(@"Bad Luck");78 } 79 } 80 81 public class
Client 82 { 83 public void TestCase1() 84 { 85 AbstractComplexProductBuilder
builder = new ConcreteProductBuilderA("Sunday", 9);86
GoodWeatherAndGoodLuckDirector director = new GoodWeatherAndGoodLuckDirector();
87 88 builder.BeginBuild(); 89
director.ConstructWithGoodWeatherAndGoodLuck(builder); 90 ComplexProduct
productWithGoodLuck = builder.EndBuild(); 91 92 builder.BeginBuild(); 93
director.ConstructWithBadWeatherAndBadLuck(builder); 94 ComplexProduct
productWithBadLuck = builder.EndBuild(); 95 } 96 } 97 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
**实现方式Builder 将构件返回给 DirectorDirector 将构件传递给 Builder
中的下一个步骤。**
Builder
逐步的构造产品,所以其接口必须足够的普遍。如果构造过程中需要访问前面已经构造了的产品构件,则
Builder 将构件返回给 Director由 Director 将构件传递给 Builder 中的下一个步骤。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace BuilderPattern.Implementation2 2 { 3 public class ComplexProduct 4 {
5 public string ValueDependOnWeather { get; set; } 6 public string
ValueDependOnFortune { get; set; } 7 } 8 9 public abstract class
AbstractComplexProductBuilder 10 { 11 protected ComplexProduct \_complexProduct;
12 13 public void BeginBuild(ComplexProduct existingComplexProduct = null) 14 {
15 if (existingComplexProduct == null) 16 \_complexProduct = new
ComplexProduct(); 17 else 18 \_complexProduct = existingComplexProduct; 19 } 20
21 public virtual string BuildValueDependOnWeatherPart(string weather) 22 { 23
// could do nothing by default 24 \_complexProduct.ValueDependOnWeather =
weather; 25 return \_complexProduct.ValueDependOnWeather; 26 } 27 28 public
virtual string BuildValueDependOnFortunePart(string luck, string
combinedWithWeather) 29 { 30 // could do nothing by default 31
\_complexProduct.ValueDependOnFortune = luck + combinedWithWeather; 32 return
\_complexProduct.ValueDependOnFortune; 33 } 34 35 public ComplexProduct
EndBuild() 36 { 37 return this.\_complexProduct; 38 } 39 } 40 41 public class
ConcreteProductBuilderA : AbstractComplexProductBuilder 42 { 43 private string
\_dayOfWeek; 44 private int \_luckyNumber; 45 46 public
ConcreteProductBuilderA(string dayOfWeek, int luckyNumber) 47 { 48 \_dayOfWeek =
dayOfWeek; 49 \_luckyNumber = luckyNumber; 50 } 51 52 public override string
BuildValueDependOnWeatherPart(string weather) 53 { 54 // something customized 55
\_complexProduct.ValueDependOnWeather = \_dayOfWeek + " is " + weather; 56
return \_complexProduct.ValueDependOnWeather; 57 } 58 59 public override string
BuildValueDependOnFortunePart(string luck, string combinedWithWeather) 60 { 61
// something customized 62 if (\_luckyNumber == 8) 63
\_complexProduct.ValueDependOnFortune = "Supper" + luck + combinedWithWeather;
64 else 65 \_complexProduct.ValueDependOnFortune = "Just so so" + luck +
combinedWithWeather; 66 return \_complexProduct.ValueDependOnFortune; 67 } 68 }
69 70 public class GoodWeatherAndGoodLuckDirector 71 { 72 public void
ConstructWithGoodWeatherAndGoodLuck(AbstractComplexProductBuilder builder) 73 {
74 string weather = builder.BuildValueDependOnWeatherPart(@"PM2.5 \< 50"); 75
builder.BuildValueDependOnFortunePart(@"Good Luck", weather); 76 } 77 78 public
void ConstructWithBadWeatherAndBadLuck(AbstractComplexProductBuilder builder) 79
{ 80 string weather = builder.BuildValueDependOnWeatherPart(@"PM2.5 \> 500"); 81
builder.BuildValueDependOnFortunePart(@"Bad Luck", weather); 82 } 83 } 84 85
public class Client 86 { 87 public void TestCase2() 88 { 89
AbstractComplexProductBuilder builder = new ConcreteProductBuilderA("Sunday",
9); 90 GoodWeatherAndGoodLuckDirector director = new
GoodWeatherAndGoodLuckDirector(); 91 92 builder.BeginBuild(); 93
director.ConstructWithGoodWeatherAndGoodLuck(builder); 94 ComplexProduct
productWithGoodLuck = builder.EndBuild(); 95 96 builder.BeginBuild(); 97
director.ConstructWithBadWeatherAndBadLuck(builder); 98 ComplexProduct
productWithBadLuck = builder.EndBuild(); 99 } 100 } 101 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码

View File

@@ -0,0 +1,122 @@
设计模式之美Chain of Responsibility职责链
**索引**
- 意图
- 结构
- 参与者
- 适用性
- 效果
- 相关模式
- 实现
- 实现方式(一):实现后继者链。
**意图**
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。
将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它位置。
Avoid coupling the sender of a request to its receiver by giving more than one
object a chance to handle the request.
Chain the receiving objects and pass the request along the chain until an object
handles it.
**结构**
![211263736074.png](media/5558a63c1bb2ff6c348c2e4ad817c713.png)
一个典型的对象结构可能如下图所示:
![212009985862.png](media/dd67bc16239c21383f68dac90175918b.png)
**参与者**
Handler
- 定义一个处理请求的接口。
- 实现后继链
ConcreteHandler
- 处理它所负责的请求。
- 可访问它的后继者。
- 如果可处理该请求,就处理;否则将该请求转发给它的后继者。
Client
- 向链上的具体处理者对象提交请求。
**适用性**
在以下情况下可以使用 Chain of Responsibility 模式:
- 有多个对象可以处理一个请求,哪个对象处理该请求运行时自动确定。
- 你想在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
- 可处理一个请求的对象集合应被动态指定。
**效果**
- 降低耦合度。对象无需知道哪个一个对象处理其请求,仅需知道对象被处理。
- 增强了给对象指派职责的灵活性。可以运行时对该链进行动态增加或修改。
**相关模式**
- Chain of Resposibility 常与 Composite
一起使用。一个构件的父构件可作为它的后继。
**实现**
**实现方式(一):实现后继者链。**
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace ChainOfResponsibilityPattern.Implementation1 2 { 3 public enum
RequestCategory 4 { 5 Category1, 6 Category2, 7 } 8 9 public abstract class
Request 10 { 11 public abstract RequestCategory Category { get; } 12 public bool
IsHandled { get; set; } 13 } 14 15 public class ConcreteRequest1 : Request 16 {
17 public override RequestCategory Category 18 { 19 get { return
RequestCategory.Category1; } 20 } 21 } 22 23 public class ConcreteRequest2 :
Request 24 { 25 public override RequestCategory Category 26 { 27 get { return
RequestCategory.Category2; } 28 } 29 } 30 31 public abstract class Handler 32 {
33 private Handler \_successor; 34 35 public Handler() 36 { 37 } 38 39 public
Handler(Handler successor) 40 { 41 \_successor = successor; 42 } 43 44 public
void Handle(Request request) 45 { 46 OnHandle(request); 47 48 if
(!request.IsHandled) 49 { 50 if (_successor != null) 51 { 52 // pass request to
successor 53 \_successor.Handle(request); 54 } 55 } 56 } 57 58 protected
abstract void OnHandle(Request request); 59 } 60 61 public class
ConcreteHandler1 : Handler 62 { 63 public ConcreteHandler1() 64 { 65 } 66 67
public ConcreteHandler1(Handler successor) 68 : base(successor) 69 { 70 } 71 72
protected override void OnHandle(Request request) 73 { 74 if (request.Category
== RequestCategory.Category1) 75 { 76 // handle the request which category is
Category1 77 request.IsHandled = true; 78 } 79 } 80 } 81 82 public class
ConcreteHandler2 : Handler 83 { 84 public ConcreteHandler2() 85 { 86 } 87 88
public ConcreteHandler2(Handler successor) 89 : base(successor) 90 { 91 } 92 93
protected override void OnHandle(Request request) 94 { 95 if (request.Category
== RequestCategory.Category2) 96 { 97 // handle the request which category is
Category2 98 request.IsHandled = true; 99 } 100 } 101 } 102 103 public class
Client 104 { 105 public void TestCase1() 106 { 107 Request request1 = new
ConcreteRequest1(); 108 Request request2 = new ConcreteRequest2(); 109 110
Handler handler2 = new ConcreteHandler2(); 111 Handler handler1 = new
ConcreteHandler1(handler2); 112 113 handler1.Handle(request1); 114
handler1.Handle(request2); 115 } 116 } 117 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码

View File

@@ -0,0 +1,316 @@
设计模式之美Command命令
**索引**
- 别名
- 意图
- 结构
- 参与者
- 适用性
- 效果
- 相关模式
- 实现
- 实现方式(一):直接注入 Receiver 对象Command 决定调用哪个方法。
- 实现方式(二):注入 Receiver 的指定方法Command 仅能调用该方法。
- 实现方式(三):参数化 Command 构造。
- 实现方式(四):使用泛型减少 Command 子类。
- 实现方式(五):使用弱引用代替对 Receiver 的强引用。
- 实现方式(六):使 Command 支持 Undo 和 Redo。
- 实现方式(七):使 MacroCommand 来管理 Command 序列。
**别名**
- Action
- Transaction
**意图**
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
Encapsulate a request as an object, thereby letting you parameterize clients
with different requests, queue or log requests, and support undoable operations.
**结构**
![315146708441.png](media/31e9acd8d4ae264b538586670f9ffed9.png)
**参与者**
Command
- 声明 Execute 操作的接口。
ConcreteCommand
- 将一个接收者对象绑定于一个动作。
- 调用接收者相应的操作,以实现 Execute。
Client
- 创建一个具体 Command 对象并设定它的接收者。
Invoker
- 要求 Command 执行请求。
Receiver
- 知道如何实施与执行一个请求相关的操作。任何类都可能作为一个接收者。
![030118743562.png](media/e9be1328d4c07a358ba694abb1297b65.png)
**适用性**
在以下情况下可以使用 Command 模式:
- Command
模式是回调callback机制的一个面向对象的替代品。所谓回调函数是指函数先在某处注册而它将在稍后某个需要的时候被调用。
- 在不同的时刻指定、排列和执行请求。Command
对象可以有一个与初始请求无关的生存期。
- 支持取消操作。需要定义 Unexecute 操作来取消 Execute 操作调用的效果。
- 支持修改日志,这样当系统崩溃时,这些修改可以被重做一遍。
- 用构建在原语操作上的高层操作构造一个系统。例如构建事务Transaction系统。
**效果**
- Command 模式将调用操作的对象与知道如何实现该操作的对象解耦。
- Command 是 first-class 对象。它们可像其他的对象一样被操纵和扩展。
- 可以将多个 Command 装配成一个复合 Command。
- 增加新的 Command 很容易,因为无需改变已有的类
**相关模式**
- Composite 模式可被用来实现 MacroCommand。
- Memento 模式可用来保持某个状态Command 用这一状态来取消它的效果。
- 可以使用 Prototype 来拷贝 Command 对象。
**实现**
**实现方式(一):直接注入 Receiver 对象Command 决定调用哪个方法。**
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace CommandPattern.Implementation1 2 { 3 public abstract class Command 4
{ 5 public abstract void Execute(); 6 } 7 8 public class ConcreteCommand :
Command 9 { 10 private Receiver \_receiver; 11 12 public
ConcreteCommand(Receiver receiver) 13 { 14 \_receiver = receiver; 15 } 16 17
public override void Execute() 18 { 19 \_receiver.Action(); 20 } 21 } 22 23
public class Receiver 24 { 25 public void Action() 26 { 27 // do something28 }
29 } 30 31 public class Invoker 32 { 33 private Command \_cmd; 34 35 public void
StoreCommand(Command cmd) 36 { 37 \_cmd = cmd; 38 } 39 40 public void Invoke()
41 { 42 if (\_cmd != null)43 { 44 \_cmd.Execute(); 45 } 46 } 47 } 48 49 public
class Client 50 { 51 public void TestCase1() 52 { 53 Receiver receiver = new
Receiver(); 54 Command cmd = new ConcreteCommand(receiver); 55 56 Invoker
invoker = new Invoker(); 57 invoker.StoreCommand(cmd); 58 59 invoker.Invoke();
60 } 61 } 62 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
**实现方式(二):注入 Receiver 的指定方法Command 仅能调用该方法。**
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace CommandPattern.Implementation2 2 { 3 public abstract class Command 4
{ 5 public abstract void Execute(); 6 } 7 8 public class ConcreteCommand :
Command 9 { 10 private Action \_action; 11 12 public ConcreteCommand(Action
action) 13 { 14 \_action = action; 15 } 16 17 public override void Execute() 18
{ 19 \_action.Invoke(); 20 } 21 } 22 23 public class Receiver 24 { 25 public
void Action() 26 { 27 // do something28 } 29 } 30 31 public class Invoker 32 {
33 private Command \_cmd; 34 35 public void StoreCommand(Command cmd) 36 { 37
\_cmd = cmd; 38 } 39 40 public void Invoke() 41 { 42 if (\_cmd != null)43 { 44
\_cmd.Execute(); 45 } 46 } 47 } 48 49 public class Client 50 { 51 public void
TestCase2() 52 { 53 Receiver receiver = new Receiver(); 54 Command cmd = new
ConcreteCommand(receiver.Action); 55 56 Invoker invoker = new Invoker(); 57
invoker.StoreCommand(cmd); 58 59 invoker.Invoke(); 60 } 61 } 62 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
**实现方式(三):参数化 Command 构造。**
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace CommandPattern.Implementation3 2 { 3 public abstract class Command 4
{ 5 public abstract void Execute(); 6 } 7 8 public class ConcreteCommand :
Command 9 { 10 private Action\<string\> \_action;11 private string \_state; 12
13 public ConcreteCommand(Action\<string\> action, string state) 14 { 15
\_action = action; 16 \_state = state; 17 } 18 19 public override void Execute()
20 { 21 \_action.Invoke(_state); 22 } 23 } 24 25 public class Receiver 26 { 27
public void Action(string state) 28 { 29 // do something30 } 31 } 32 33 public
class Invoker 34 { 35 private Command \_cmd; 36 37 public void
StoreCommand(Command cmd) 38 { 39 \_cmd = cmd; 40 } 41 42 public void Invoke()
43 { 44 if (\_cmd != null)45 { 46 \_cmd.Execute(); 47 } 48 } 49 } 50 51 public
class Client 52 { 53 public void TestCase3() 54 { 55 Receiver receiver = new
Receiver(); 56 Command cmd = new ConcreteCommand(receiver.Action, "Hello
World");57 58 Invoker invoker = new Invoker(); 59 invoker.StoreCommand(cmd); 60
61 invoker.Invoke(); 62 } 63 } 64 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
**实现方式(四):使用泛型减少 Command 子类。**
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace CommandPattern.Implementation4 2 { 3 public abstract class Command 4
{ 5 public abstract void Execute(); 6 } 7 8 public class ConcreteCommand\<T, S\>
: Command 9 { 10 private Action\<T, S\> \_action; 11 private T \_state1; 12
private S \_state2; 13 14 public ConcreteCommand(Action\<T, S\> action, T
state1, S state2) 15 { 16 \_action = action; 17 \_state1 = state1; 18 \_state2 =
state2; 19 } 20 21 public override void Execute() 22 { 23
\_action.Invoke(_state1, \_state2); 24 } 25 } 26 27 public class Receiver 28 {
29 public void Action(string state1, int state2) 30 { 31 // do something32 } 33
} 34 35 public class Invoker 36 { 37 private Command \_cmd; 38 39 public void
StoreCommand(Command cmd) 40 { 41 \_cmd = cmd; 42 } 43 44 public void Invoke()
45 { 46 if (\_cmd != null)47 { 48 \_cmd.Execute(); 49 } 50 } 51 } 52 53 public
class Client 54 { 55 public void TestCase4() 56 { 57 Receiver receiver = new
Receiver(); 58 Command cmd = new ConcreteCommand\<string, int\>(59
receiver.Action, "Hello World", 250);60 61 Invoker invoker = new Invoker(); 62
invoker.StoreCommand(cmd); 63 64 invoker.Invoke(); 65 } 66 } 67 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
**实现方式(五):使用弱引用代替对 Receiver 的强引用。**
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace CommandPattern.Implementation5 2 { 3 public class WeakAction 4 { 5
public WeakAction(Action action) 6 { 7 Method = action.Method; 8 Reference = new
WeakReference(action.Target); 9 } 10 11 protected MethodInfo Method { get;
private set; }12 protected WeakReference Reference { get; private set; }13 14
public bool IsAlive 15 { 16 get { return Reference.IsAlive; } 17 } 18 19 public
object Target 20 { 21 get { return Reference.Target; } 22 } 23 24 public void
Invoke() 25 { 26 if (Method != null && IsAlive) 27 { 28 Method.Invoke(Target,
null);29 } 30 } 31 } 32 33 public abstract class Command 34 { 35 public abstract
void Execute(); 36 } 37 38 public class ConcreteCommand : Command 39 { 40
private WeakAction \_action; 41 42 public ConcreteCommand(Action action) 43 { 44
\_action = new WeakAction(action); 45 } 46 47 public override void Execute() 48
{ 49 \_action.Invoke(); 50 } 51 } 52 53 public class Receiver 54 { 55 public
void Action() 56 { 57 // do something58 } 59 } 60 61 public class Invoker 62 {
63 private Command \_cmd; 64 65 public void StoreCommand(Command cmd) 66 { 67
\_cmd = cmd; 68 } 69 70 public void Invoke() 71 { 72 if (\_cmd != null)73 { 74
\_cmd.Execute(); 75 } 76 } 77 } 78 79 public class Client 80 { 81 public void
TestCase5() 82 { 83 Receiver receiver = new Receiver(); 84 Command cmd = new
ConcreteCommand(receiver.Action); 85 86 Invoker invoker = new Invoker(); 87
invoker.StoreCommand(cmd); 88 89 invoker.Invoke(); 90 } 91 } 92 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
**实现方式(六):使 Command 支持 Undo 和 Redo。**
如果 Command 提供方法逆转操作,例如 Undo
操作就可以取消执行的效果。为达到这个目的ConcreteCommand
类可能需要存储额外的状态信息。
这个状态包括:
接收者对象,它真正执行处理该请求的各操作。
接收者上执行操作的参数。
如果处理请求的操作会改变接收者对象中的某些值,那么这些值也必须先存储起来。接收者还必须提供一些操作,以使该命令可将接收者恢复到它先前的状态。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace CommandPattern.Implementation6 2 { 3 public abstract class Command 4
{ 5 public abstract void Execute(); 6 public abstract void Unexecute(); 7 public
abstract void Reexecute(); 8 } 9 10 public class ConcreteCommand : Command 11 {
12 private Receiver \_receiver; 13 private string \_state; 14 private string
\_lastState; 15 16 public ConcreteCommand(Receiver receiver, string state) 17 {
18 \_receiver = receiver; 19 \_state = state; 20 } 21 22 public override void
Execute() 23 { 24 \_lastState = \_receiver.Name; 25
\_receiver.ChangeName(_state); 26 } 27 28 public override void Unexecute() 29 {
30 \_receiver.ChangeName(\_lastState); 31 \_lastState = string.Empty;32 } 33 34
public override void Reexecute() 35 { 36 Unexecute(); 37 Execute(); 38 } 39 } 40
41 public class Receiver 42 { 43 public string Name { get; private set; }44 45
public void ChangeName(string name) 46 { 47 // do something48 Name = name; 49 }
50 } 51 52 public class Invoker 53 { 54 private Command \_cmd; 55 56 public void
StoreCommand(Command cmd) 57 { 58 \_cmd = cmd; 59 } 60 61 public void Invoke()
62 { 63 if (\_cmd != null)64 { 65 \_cmd.Execute(); 66 } 67 } 68 69 public void
UndoInvoke() 70 { 71 if (\_cmd != null)72 { 73 \_cmd.Unexecute(); 74 } 75 } 76 }
77 78 public class Client 79 { 80 public void TestCase6() 81 { 82 Receiver
receiver = new Receiver(); 83 Command cmd = new ConcreteCommand(receiver, "Hello
World");84 85 Invoker invoker = new Invoker(); 86 invoker.StoreCommand(cmd); 87
88 invoker.Invoke(); 89 invoker.UndoInvoke(); 90 } 91 } 92 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
**实现方式(七):使 MacroCommand 来管理 Command 序列。**
MacroCommand 需要提供增加和删除子 Command 的操作。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace CommandPattern.Implementation7 2 { 3 public abstract class Command 4
{ 5 public abstract void Execute(); 6 } 7 8 public class MacroCommand : Command
9 { 10 private List\<Command\> \_cmdList = new List\<Command\>(); 11 12 public
MacroCommand() 13 { 14 } 15 16 public void Add(Command cmd) 17 { 18
\_cmdList.Add(cmd); 19 } 20 21 public void Remove(Command cmd) 22 { 23
\_cmdList.Remove(cmd); 24 } 25 26 public override void Execute() 27 { 28 foreach
(var cmd in \_cmdList) 29 { 30 cmd.Execute(); 31 } 32 } 33 } 34 35 public class
ConcreteCommand1 : Command 36 { 37 private Receiver \_receiver; 38 39 public
ConcreteCommand1(Receiver receiver) 40 { 41 \_receiver = receiver; 42 } 43 44
public override void Execute() 45 { 46 \_receiver.Action1(); 47 } 48 } 49 50
public class ConcreteCommand2 : Command 51 { 52 private Receiver \_receiver; 53
54 public ConcreteCommand2(Receiver receiver) 55 { 56 \_receiver = receiver; 57
} 58 59 public override void Execute() 60 { 61 \_receiver.Action2(); 62 } 63 }
64 65 public class Receiver 66 { 67 public void Action1() 68 { 69 // do
something 70 } 71 72 public void Action2() 73 { 74 // do something 75 } 76 } 77
78 public class Invoker 79 { 80 private Command \_cmd; 81 82 public void
StoreCommand(Command cmd) 83 { 84 \_cmd = cmd; 85 } 86 87 public void Invoke()
88 { 89 if (\_cmd != null) 90 { 91 \_cmd.Execute(); 92 } 93 } 94 } 95 96 public
class Client 97 { 98 public void TestCase7() 99 { 100 Receiver receiver = new
Receiver(); 101 Command cmd1 = new ConcreteCommand1(receiver); 102 Command cmd2
= new ConcreteCommand2(receiver); 103 MacroCommand macro = new MacroCommand();
104 macro.Add(cmd1); 105 macro.Add(cmd2); 106 107 Invoker invoker = new
Invoker(); 108 invoker.StoreCommand(macro); 109 110 invoker.Invoke(); 111 } 112
} 113 }

View File

@@ -0,0 +1,155 @@
设计模式之美Composite组合
**索引**
- 意图
- 结构
- 参与者
- 适用性
- 缺点
- 效果
- 相关模式
- 实现
- 实现方式(一):在 Component 中定义公共接口以保持透明性但损失安全性。
**意图**
将对象组合成树形结构以表示 “部分-整体” 的层次结构。
Composite 使得用户对于单个对象和组合对象的使用具有一致性。
Compose objects into tree structures to represent part-whole hierarchies.
Composite lets clients treat individual objects and compositions of objects
uniformly.
**结构**
![120166157079.png](media/c03017c41ac99aa131b7e675f1022124.png)
典型的 Composite 对象结构:
![120529122324.png](media/f2793f123ffd3e6517ffca83c3b4218e.png)
**参与者**
Component
- 为组合中的对象声明接口。
- 在适当的情况下,实现所有类共有接口的缺省行为
- 声明一个接口用于访问和管理 Component 的子组件。
- 在递归结构中定义一个接口,用于访问一个父部件,并在合适的情况下实现它。
Leaf
- 在组合中表示叶节点对象,叶节点没有子节点。
- 在组合中定义图元对象的行为。
Composite
- 定义有子部件的那些部件的行为。
- 在 Composite 接口中实现与子部件有关的操作。
Client
- 通过 Component 接口操纵组合部件的对象。
**适用性**
在以下情况下可以使用 Composite 模式:
- 你想表示对象的 “部分-整体” 层次结构。
- 你希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。
**缺点**
- 与类层次结构设计原则冲突
Composite 模式的目的之一是使得用户不知道它们正在使用具体的 Leaf 和 Composite
类。
为达到这一目的Component 需要为 Leaf 和 Composite
定义一些公共操作,并提供缺省的实现,而 Leaf 和 Composite
子类可以对它们进行重定义。
然而,这个目标会与类层次结构设计原则冲突,该原则规定:一个类只能定义那些对它的子类有意义的操作。
**效果**
- 定义了包含基本对象和组合对象的类层次结构。
- 简化客户代码。
- 使得更容易增加新类型的组件。
- 使你的设计变得更加一般化。
**相关模式**
- Command 模式描述了如何用一个 MacroCommand Composite 类组成一些 Command
对象,并对它们进行排序。
- 通常 “部件-父部件” 连接用于 Responsibility of Chain 模式。
- Decorator 模式经常与 Composite 模式一起使用。它们通常有一个公共的父类。
- Flyweight 让你共享组件,但不再能引用它们的父部件。
- Iterator 可以用来遍历 Composite。
- Visitor 将本来应该分布在 Composite 和 Leaf 类中的操作和行为局部化。
**实现**
**实现方式(一):在 Component 中定义公共接口以保持透明性但损失安全性。**
在 Component 中定义 Add 和 Remove 操作需要考虑安全性和透明性。
在类层次结构的根部定义子节点管理接口的方法具有良好的透明性,但是这一方法是以安全性为代价的,因为客户有可能会做一些无意义的事情,例如在
Leaf 中 Add 对象等。
在 Composite
类中定义管理子部件的方法具有良好的安全性,但是这又损失了透明性,因为 Leaf 和
Composite 具有不同的接口。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace CompositePattern.Implementation1 2 { 3 public abstract class
Component 4 { 5 protected List\<Component\> \_children = new
List\<Component\>(); 6 7 public abstract void Operation(); 8 9 public virtual
void Add(Component component) 10 { 11 \_children.Add(component); 12 } 13 14
public virtual void Remove(Component component) 15 { 16
\_children.Remove(component); 17 } 18 19 public virtual IEnumerable\<Component\>
GetChildren() 20 { 21 return \_children; 22 } 23 } 24 25 public class Leaf :
Component 26 { 27 public override void Operation() 28 { 29 // do something30 }
31 32 public override void Add(Component component) 33 { 34 throw new
InvalidOperationException(); 35 } 36 37 public override void Remove(Component
component) 38 { 39 throw new InvalidOperationException(); 40 } 41 42 public
override IEnumerable\<Component\> GetChildren() 43 { 44 throw new
InvalidOperationException(); 45 } 46 } 47 48 public class Composite : Component
49 { 50 public override void Operation() 51 { 52 foreach (var child in
\_children) 53 { 54 child.Operation(); 55 } 56 // may do something57 } 58 } 59
60 public class Client 61 { 62 public void TestCase1() 63 { 64 Component
component1 = new Leaf(); 65 Component component2 = new Composite(); 66 67
component2.Add(component1); 68 69 component1.Operation(); 70
component2.Operation(); 71 } 72 } 73 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码

View File

@@ -0,0 +1,88 @@
设计模式之美Creational Patterns创建型模式
创建型模式Creational Patterns抽象了对象实例化过程。
它们帮助一个系统独立于如何创建、组合和表示它的那些对象。
- 一个类创建型模式使用继承改变被实例化的类。
- 一个对象创建型模式将实例化委托给另一个对象。
随着系统演化得越来越依赖于对象复合而不是类的继承,创建型模式变得更为重要。
在这些模式中,有两个不断出现的主旋律:
1. 它们都将关于该系统使用那些具体的类的信息封装起来。
2. 它们隐藏了这些类的实例是如何被创建和放在一起的。
**因此,创建型模式在什么被创建,谁创建它,它是怎样被创建的,以及何时创建这些方面给予你很大的灵活性。**
**Consequently, the creational patterns give you a lot of flexibility in what
gets created, who creates it, how it gets created, and when.**
- **Factory Method (工厂方法)**
- Define an interface for creating an object, but let subclasses decide
which class to instantiate. Factory Method lets a class defer
instantiation to subclasses.
- 定义一个用于创建目标对象的接口让子类决定实例化哪一个目标类。Factory
Method 使一个类的实例化延迟到其子类。
- **Prototype (原型)**
- Specify the kinds of objects to create using a prototypical instance,
and create new objects by copying this prototype.
- 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
- **Abstract Factory (抽象工厂)**
- Provide an interface for creating families of related or dependent
objects without specifying their concrete classes.
- 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
- **Builder (生成器)**
- Separate the construction of a complex object from its representation so
that the same construction process can create different representations.
- 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
- **Singleton (单件)**
- Ensure a class only has one instance, and provide a global point of
access to it.
- 保证一个类仅有一个实例,并提供一个访问它的全局访问点。
用一个系统创建的那些对象的类对系统进行参数化有两种方法。
**方法一生成创建对象的类的子类Subclass这对应于使用 Factory Method
模式。**
这种方法的缺点是,仅为了改变产品类,就可能需要创建一个新的子类。这样的改变可能是级联的。
**方法二对系统进行参数化的方法依赖于对象复合Composition定义一个对象负责明确产品对象的类并将它作为该系统的参数。**
这是 Abstract Factory、Builder、Prototype 模式的关键特征。
这三种模式都涉及到创建一个新的负责创建产品对象的“工厂对象”。
- Abstract Factory 由这个工厂对象产生多个类的对象。
- Builder 由这个工厂对象使用一个相对复杂的协议,逐步创建一个复杂产品。
- Prototype
由该工厂对象通过拷贝原型对象来创建产品对象。在这种情况下,因为原型负责返回产品对象,所以工厂对象和原型是同一个对象。
Factory Method 使一个设计可以定制且只略微有一些复杂。其他设计模式需要新的类,而
Factory Method 只需要一个新的操作。
使用 Abstract Factory、Builder、Prototype 的设计比使用 Factory Method
的设计更灵活,但它们也更加复杂。
**通常,设计以使用 Factory Method
开始,并且当设计者发现需要更大的灵活性时,设计便会向其他创建型模式演化。**

View File

@@ -0,0 +1,161 @@
设计模式之美Decorator装饰
**索引**
- 别名
- 意图
- 结构
- 参与者
- 适用性
- 缺点
- 效果
- 相关模式
- 实现
- 实现方式Decorator 对象的接口必须与它所装饰的 Component
的接口保持一致。
- 实现方式(二):省略抽象的 Decorator 类。
**别名**
- 包装器Wrapper
**意图**
动态地给一个对象添加一些额外的职责。
就增加功能来说Decorator 模式相比生成子类更为灵活。
Attach additional responsibilities to an object dynamically.
Decorators provide a flexible alternative to subclassing for extending
functionality.
**结构**
![225319129467.png](media/688b6a2cfb78ed1f0b1a37b98f116dc9.png)
**参与者**
Component
- 定义一个对象接口,可以给这些对象动态地添加职责。
ConcreteComponent
- 定义一个对象,可以给这个对象添加一些职责。
Decorator
- 维持一个指向 Component 对象的指针,并定义一个与 Component 接口一致的接口。
ConcreteDecorator
- 向组件添加职责。
**适用性**
在以下情况下可以使用 Decorator 模式:
- 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
- 处理那些可以撤销的职责。
- 当不能采用生成子类的方法进行扩充时。
**缺点**
- Decorator 是一个透明的包装,其与 Component 还是有些差别的。
- 采用 Decorator
模式进行系统设计往往会产生许多看上去类似的小对象。导致很难学习系统,排错也很困难。
**效果**
- 比静态继承更灵活。
- 避免在层次结构高层的类有太多的特征。
**相关模式**
- Decorator 模式不同于 Adapter 模式,因为 Decorator
仅改变对象的职责而不改变它的接口,而 Adapter 将给对象一个全新的接口。
- 可以将 Decorator 视为一个退化的、仅有一个组件的 Composite。然而Decorator
仅给对象添加额外的职责,它的目的不在于对象聚集。
- 用一个 Decorator 可以改变对象的外表,而 Strategy
模式使得你可以改变对象的内核。这是改变对象的两种途径。
- 当 Component 类原本就很庞大时,使用 Decorator 模式的代价太高Strategy
模式相对更好一些。
**实现**
**实现方式Decorator 对象的接口必须与它所装饰的 Component
的接口保持一致。**
所有的 ConcreteDecorator 类必须有一个公共的父类。
使用 Decorator
模式仅从外部改变组件,因此组件无需对它的装饰有任何了解,也就是说,这些装饰对该组件是透明的。
![249384122421.png](media/0ed3006df19ccbbfe704a011872d66c4.png)
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace DecoratorPattern.Implementation1 2 { 3 public abstract class
Component 4 { 5 public abstract void Operation(); 6 } 7 8 public class
ConcreteComponent : Component 9 { 10 public override void Operation() 11 { 12 //
do something13 } 14 } 15 16 public abstract class Decorator : Component 17 { 18
private Component \_component; 19 20 public Decorator(Component component) 21 {
22 \_component = component; 23 } 24 25 public override void Operation() 26 { 27
\_component.Operation(); 28 } 29 } 30 31 public class ConcreteDecorator :
Decorator 32 { 33 public ConcreteDecorator(Component component) 34 :
base(component)35 { 36 } 37 38 public override void Operation() 39 { 40
base.Operation();41 AddedBehavior(); 42 } 43 44 private void AddedBehavior() 45
{ 46 // do some other things47 } 48 } 49 50 public class Client 51 { 52 public
void TestCase1() 53 { 54 Component component1 = new ConcreteComponent(); 55
Component component2 = new ConcreteDecorator(component1); 56 57
component2.Operation(); 58 } 59 } 60 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
**实现方式(二):省略抽象的 Decorator 类。**
当仅需要添加一个职责是,没有必要定义抽象的 Decorator 类。
这时可以把 Decorator 向 Component 转发请求的职责合并到 ConcreteDecorator 中。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace DecoratorPattern.Implementation2 2 { 3 public abstract class
Component 4 { 5 public abstract void Operation(); 6 } 7 8 public class
ConcreteComponent : Component 9 { 10 public override void Operation() 11 { 12 //
do something13 } 14 } 15 16 public class ConcreteDecorator : Component 17 { 18
private Component \_component; 19 20 public ConcreteDecorator(Component
component) 21 { 22 \_component = component; 23 } 24 25 public override void
Operation() 26 { 27 \_component.Operation(); 28 AddedBehavior(); 29 } 30 31
private void AddedBehavior() 32 { 33 // do some other things34 } 35 } 36 37
public class Client 38 { 39 public void TestCase1() 40 { 41 Component component1
= new ConcreteComponent(); 42 Component component2 = new
ConcreteDecorator(component1); 43 44 component2.Operation(); 45 } 46 } 47 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码

View File

@@ -0,0 +1,106 @@
**索引**
- 别名
- 意图
- 结构
- 参与者
- 适用性
- 效果
- 实现
- 实现方式Dynamic Property 的示例实现。
**别名**
- Property
- Properties
- Property List
**意图**
使对象可以为客户提供广泛且可扩展的属性集合。
Lets an object provides a generic and extensible set of properties to clients.
**结构**
![245246206432.png](media/0351faeada38866d0a76a2d60ff2e9b3.png)
**参与者**
Object
- 目标对象可存储 Property 列表。
- 可使用不同的类型来作为 Property 的标识符,最简单的可以使用 string 类型。
Property
- 属性定义。
**适用性**
当以下情况成立时可以使用 Dynamic Property 模式:
- 当对象需要定义大量属性时。
- 当对象的属性是运行时可变时。
**效果**
- 可在运行时动态的修改对象的属性。
**实现**
**实现方式Dynamic Property 的示例实现。**
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace DynamicPropertyPattern.Implementation1 2 { 3 public class Person 4 {
5 private PropertyList \_properties = new PropertyList(null); 6 7 public
Property GetProperty(string propertyName) 8 { 9 return
\_properties.GetProperty(propertyName); 10 } 11 12 public bool
HasProperty(string propertyName) 13 { 14 return
\_properties.HasProperty(propertyName); 15 } 16 17 public void
AddProperty(string propertyName, Property property) 18 { 19
\_properties.AddProperty(propertyName, property); 20 } 21 22 public void
RemoveProperty(string propertyName) 23 { 24
\_properties.RemoveProperty(propertyName); 25 } 26 27 public
IEnumerable\<Property\> GetAllProperties() 28 { 29 return
\_properties.GetAllProperties(); 30 } 31 } 32 33 public class Property 34 { 35
public string Name { get; set; } 36 public string Value { get; set; } 37 } 38 39
public class PropertyList 40 { 41 private PropertyList \_parent; 42 private
Dictionary\<string, Property\> \_properties 43 = new Dictionary\<string,
Property\>(); 44 45 public PropertyList(PropertyList parent) 46 { 47 \_parent =
parent; 48 } 49 50 public PropertyList Parent 51 { 52 get 53 { 54 return
\_parent; 55 } 56 } 57 58 public Property GetProperty(string propertyName) 59 {
60 if (\_properties.ContainsKey(propertyName)) 61 return
\_properties[propertyName]; 62 if (_parent != null &&
\_parent.HasProperty(propertyName)) 63 return
\_parent.GetProperty(propertyName); 64 return null; 65 } 66 67 public bool
HasProperty(string propertyName) 68 { 69 return
(\_properties.ContainsKey(propertyName)) 70 \|\| (_parent != null &&
\_parent.HasProperty(propertyName)); 71 } 72 73 public void AddProperty(string
propertyName, Property property) 74 { 75 \_properties.Add(propertyName,
property); 76 } 77 78 public void RemoveProperty(string propertyName) 79 { 80
\_properties.Remove(propertyName); 81 } 82 83 public IEnumerable\<Property\>
GetAllProperties() 84 { 85 List\<Property\> properties = new List\<Property\>();
86 87 if (_parent != null) 88 properties.AddRange(\_parent.GetAllProperties());
89 90 properties.AddRange(\_properties.Values); 91 92 return properties; 93 } 94
} 95 96 public class Client 97 { 98 public void TestCase1() 99 { 100 Person
dennis = new Person(); 101 dennis.AddProperty("Contact", new Property() { Name =
"Contact", Value = "Beijing" }); 102 dennis.AddProperty("Age", new Property() {
Name = "Age", Value = "18" }); 103 dennis.AddProperty("Gender", new Property() {
Name = "Gender", Value = "Male" }); 104 105 if
(dennis.HasProperty("Contact"))106 { 107 Property property =
dennis.GetProperty("Contact");108 } 109 } 110 } 111 }

View File

@@ -0,0 +1,153 @@
**索引**
- 意图
- 结构
- 参与者
- 适用性
- 效果
- 相关模式
- 实现
- 实现方式(一):使用示例结构实现 Extension Object。
- 实现方式(二):使用泛型实现 IExtensibleObject\<T\> 接口。
**意图**
预期对象的接口将在未来被扩展。通过额外的接口来定义扩展对象。
Anticipate that an objects interface needs to be extended in the future.
Additional interfaces are defined by extension objects..
**结构**
![115388104198.png](media/6d9c1fed25d022e36715958e50c87e30.png)
**参与者**
Subject
- 定义抽象主体对象。其定义用于查询是否包含特定扩展的接口。
Extension
- 所有扩展类的抽象基类。可以定义负责管理扩展自身的操作。
- Extension 知道其是谁的扩展。
ConcreteSubject
- 具体的主体类。实现基类定义的 GetExtension 方法并返回相应的扩展对象。
AbstractExtension
- 特定种类的扩展类的抽象基类。
ConcreteExtension
- 继承并实现 AbstractionExtension 类。实现特定的扩展功能。
**适用性**
当以下情况成立时可以使用 Extension Object 模式:
- 当需要为已有对象额外添加全新的或无法预见的接口时。
- 抽象的主要接口在不同的客户类中拥有不同的角色时。
- 无法通过子类型化来扩展接口行为时。
**效果**
- 扩展对象促进了增加新扩展功能。
- 类的主要抽象接口不会过于膨胀。
- 接口的主要抽象在不同的子系统中可实现不同的角色。
- 客户类变得更加复杂。
- 需要控制对扩展对象的滥用。
**相关模式**
- Visitor 模式可以为层级的类结构增加新的行为。Visitor 模式与 Extension Object
模式拥有类似的益处。相比 Visitor 模式Extension Object
模式不需要一个稳固的类层级结构,并且也不引入循环依赖。
- Decorator 模式是另一个可以扩展类的行为的模式。客户类在使用 Decorator
对象时的透明性比使用 Extension Object
更好。在使用窄接口或需要增强已知接口时更适合使用 Decorator 模式。
- Adapter 模式支持适配一个已知接口。Extension Object
模式支持附加的接口。当对象需要对扩展接口进行适配时可以同时使用 Extension
Object 模式和 Adapter 模式。
**实现**
**实现方式(一):使用示例结构实现 Extension Object。**
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace ExtensionObjectPattern.Implementation1 2 { 3 public abstract class
Subject 4 { 5 public abstract void Operation1(); 6 public abstract Extension
GetExtension(string extensionType); 7 } 8 9 public abstract class Extension 10 {
11 protected Subject \_owner; 12 13 public Extension(Subject owner) 14 { 15
\_owner = owner; 16 } 17 18 public abstract void DoSomething(); 19 } 20 21
public abstract class AbstractExtension : Extension 22 { 23 public
AbstractExtension(Subject owner) 24 : base(owner)25 { 26 } 27 } 28 29 public
class ConcreteExtension : AbstractExtension 30 { 31 public
ConcreteExtension(Subject owner) 32 : base(owner)33 { 34 } 35 36 public override
void DoSomething() 37 { 38 // do something39 \_owner.Operation1(); 40 } 41 } 42
43 public class ConcreteSubject : Subject 44 { 45 private ConcreteExtension
\_extension; 46 47 public ConcreteSubject() 48 { 49 \_extension = new
ConcreteExtension(this);50 } 51 52 public override void Operation1() 53 { 54 //
do something55 } 56 57 public override Extension GetExtension(string
extensionType) 58 { 59 if (extensionType == "some type")60 { 61 return
this._extension;62 } 63 64 return null;65 } 66 } 67 68 public class Client 69 {
70 public void TestCase1() 71 { 72 Subject subject = new ConcreteSubject(); 73
Extension extension = subject.GetExtension("some type");74
extension.DoSomething(); 75 } 76 } 77 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
**实现方式(二):使用泛型实现 IExtensibleObject\<T\> 接口。**
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace ExtensionObjectPattern.Implementation2 2 { 3 public abstract class
Subject : IExtensibleObject\<Subject\> 4 { 5 public abstract void Operation1();
6 public abstract IEnumerable\<IExtension\<Subject\>\> Extensions { get; } 7 } 8
9 public interface IExtensibleObject\<T\> 10 where T : class,
IExtensibleObject\<T\>11 { 12 IEnumerable\<IExtension\<T\>\> Extensions { get;
}13 } 14 15 public interface IExtension\<T\> 16 where T : class,
IExtensibleObject\<T\>17 { 18 void Attach(T owner); 19 void Detach(T owner); 20
} 21 22 public abstract class Extension : IExtension\<Subject\> 23 { 24
protected List\<Subject\> \_owners; 25 26 public void Attach(Subject owner) 27 {
28 \_owners.Add(owner); 29 } 30 31 public void Detach(Subject owner) 32 { 33
\_owners.Remove(owner); 34 } 35 36 public abstract void DoSomething(); 37 } 38
39 public class ConcreteExtension : Extension 40 { 41 public override void
DoSomething() 42 { 43 // do something44 foreach (var item in \_owners) 45 { 46
item.Operation1(); 47 } 48 } 49 } 50 51 public class ConcreteSubject : Subject
52 { 53 private List\<Extension\> \_extensions = new List\<Extension\>(); 54 55
public ConcreteSubject() 56 { 57 Extension extension = new ConcreteExtension();
58 extension.Attach(this);59 60 \_extensions.Add(extension); 61 } 62 63 public
override void Operation1() 64 { 65 // do something66 } 67 68 public override
IEnumerable\<IExtension\<Subject\>\> Extensions 69 { 70 get71 { 72 return
\_extensions; 73 } 74 } 75 } 76 77 public class Client 78 { 79 public void
TestCase1() 80 { 81 Subject subject = new ConcreteSubject(); 82 83 foreach (var
extension in subject.Extensions) 84 { 85 (extension as Extension).DoSomething();
86 } 87 } 88 } 89 }

View File

@@ -0,0 +1,113 @@
设计模式之美Facade外观
**索引**
- 意图
- 结构
- 参与者
- 适用性
- 效果
- 相关模式
- 实现
- 实现方式(一):用抽象类定义 Facade 而使子类对应于不同的子系统。
**意图**
为子系统中的一组接口提供一个一致的界面Facade
模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
Provide a unified interface to a set of interfaces in a subsystem. Facade
defines a higher-level interface that makes the subsystem easier to use.
**结构**
![000329435265.png](media/d95ed5b46b2cab7212804a212475ebef.png)
**参与者**
Facade
- 知道哪些子系统类负责处理请求。
- 将客户的请求代理给适当的子系统对象。
Subsystem Classes
- 实现子系统的功能。
- 处理由 Facade 对象指派的任务。
- 没有 Facade 的任何相关信息。
**适用性**
在以下情况下可以使用 Facade 模式:
- 当你要为一个复杂子系统提供一个简单接口时。
- 客户程序与抽象类的实现部分之间存在着很大的依赖性。
- 当你需要构建一个层次结构的子系统时,使用 Facade
模式定义子系统中每层的入口点。
**效果**
- 它对客户屏蔽子系统组件,使用 Facade 的客户程序不需要直接访问子系统对象。
- 它实现了子系统与客户之间的松耦合关系,而子系统内部的功能组件往往是紧耦合的。
- 如果应用需要,它并不限制它们使用子系统类。因此你可以在系统易用性和通用性之间加以选择。
**相关模式**
- Abstract Factory 模式可以与 Facade
模式一起使用以提供一个接口,这一接口可用来以一种子系统独立的方式创建子系统对象。
- Mediator 模式与 Facade
模式的相似之处是它抽象了一些已有的类的功能。Mediator
的目的是对同事之间的任意通讯进行抽象通常集中不属于任何单个对象的功能。Facade
模式仅对子系统接口进行抽象,并不定义新功能。
- 通常来讲,可能仅需要一个 Facade 对象,因此可以用 Singleton 模式定义 Facade。
**实现**
**实现方式(一):用抽象类定义 Facade 而使子类对应于不同的子系统。**
将一个系统划分成若干个子系统有利于降低系统的复杂性。一个常见的设计目标是使子系统间的通信和相互依赖关系达到最小。
大多数模式使用时都会产生更多更小的类。这使得子系统更具可重用性,也更容易对子系统进行定制,但这也给那些不需要定制子系统的用户带来一些使用上的困难。
Facade
可以提供一个简单的缺省视图,这一视图对于大多数用户来说已经足够,而那些需要更多的可定制性的用户可以越过
Facade 层。
用抽象类实现 Facade
而它的具体子类对应于不同的子系统实现,这可以进一步降低客户与子系统的耦合度。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace FacadePattern.Implementation1 2 { 3 public abstract class Facade 4 {
5 public abstract void Operation(); 6 } 7 8 public class ConcreteFacade : Facade
9 { 10 public override void Operation() 11 { 12 // we could use any factory
here13 // or use IoC here14 SubsystemClassA subsystemClassA = new
SubsystemClassA(); 15 SubsystemClassB subsystemClassB = new SubsystemClassB();
16 17 subsystemClassA.BehaviorA(); 18 subsystemClassB.BehaviorB(); 19 } 20 } 21
22 public class SubsystemClassA 23 { 24 public void BehaviorA() 25 { 26 // do
something27 } 28 } 29 30 public class SubsystemClassB 31 { 32 public void
BehaviorB() 33 { 34 // do something35 } 36 } 37 38 public class Client 39 { 40
public void TestCase1() 41 { 42 Facade facade = new ConcreteFacade(); 43
facade.Operation(); 44 } 45 } 46 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码

View File

@@ -0,0 +1,212 @@
设计模式之美Factory Method工厂方法
**索引**
- 别名
- 意图
- 结构
- 参与者
- 适用性
- 缺点
- 效果
- 相关模式
- 命名约定
- 实现
- 实现方式Creator
类是一个抽象类并且不提供它所声明的工厂方法的实现。
- 实现方式Creator 类是一个具体类而且为工厂方法提供一个缺省的实现。
- 实现方式(三):参数化工厂方法。
- 实现方式(四):使用模板以避免创建子类。
**别名**
- 虚构造器 Virtual Constructor
**意图**
定义一个用于创建目标对象的接口,让子类决定实例化哪一个目标类。
Factory Method 使一个类的实例化延迟到其子类。
Define an interface for creating an object, but let subclasses decide which
class to instantiate.
Factory Method lets a class defer instantiation to subclasses.
**结构**
![956473094863.png](media/2270a94077e069568d686166f5101fc9.png)
**参与者**
Product
- 定义工厂方法所创建的对象的接口Interface
ConcreteProduct
- 实现 Product 接口。
Creator
- 声明工厂方法,该方法返回一个 Product 类型的对象。 Creator
也可以定义一个工厂方法的缺省实现,它返回一个缺省的 ConcreteProduct 对象。
- 可以调用工厂方法以创建一个 Product 对象。
ConcreteCreator
- 重定义Override工厂方法以创建一个 Product 对象。
**适用性**
在下列情况下可以使用 Factory Method 模式:
- 当一个类不知道它所必须创建的对象的类的时候。
- 当一个类希望由它的子类来指定它所创建的对象的时候。
- 当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。
**缺点**
- 客户可能仅仅为了创建一个特定的 ConcreteProduct 对象,就不得不创建 Creator
的子类。
**效果**
- 为子类提供挂钩
- 连接平行的类层次
**相关模式**
- Abstract Factory 经常用工厂方法来实现。
- Factory Method 通常在 [Template
Method](http://www.cnblogs.com/gaochundong/p/design_pattern_template_method.html)
中被调用。
- Prototype 不需要创建 Creator 的子类。但是,它们通常要求一个针对 Product 类的
Initialize 操作。Creator 使用 Initialize 来初始化对象,而 Factory Method
不需要这样的操作。
**命名约定**
使用命名约定是一个好习惯它可以清楚地说明你正在使用工厂方法。Convention over
Configuration
例如,总是声明那些定义为工厂方法的抽象操作为 CreateProduct()。
**实现**
**实现方式Creator 类是一个抽象类并且不提供它所声明的工厂方法的实现。**
需要子类来定义实现,因为没有合理的缺省实现。它避免了不得不实例化不可预见类的问题。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace FactoryMethodPattern.Implementation1 2 { 3 public abstract class
AbstractOrInterfaceOfCreator 4 { 5 public abstract AbstractOrInterfaceOfProduct
CreateProduct(); 6 } 7 8 public abstract class AbstractOrInterfaceOfProduct 9 {
10 } 11 12 public class ConcreteCreator : AbstractOrInterfaceOfCreator 13 { 14
public override AbstractOrInterfaceOfProduct CreateProduct() 15 { 16 return new
ConcreteProduct(); 17 } 18 } 19 20 public class ConcreteProduct :
AbstractOrInterfaceOfProduct 21 { 22 } 23 24 public partial class Test 25 { 26
public void Case1() 27 { 28 AbstractOrInterfaceOfCreator creator = new
ConcreteCreator(); 29 AbstractOrInterfaceOfProduct product =
creator.CreateProduct(); 30 } 31 } 32 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
**实现方式Creator 类是一个具体类而且为工厂方法提供一个缺省的实现。**
具体的 Creator
主要因为灵活性才使用工厂方法。它所遵循的准则是,“用一个独立的操作创建对象,这样子类才能重定义它们的创建方法”。这条准则保证了子类的设计者能够在必要的时候改变父类所实例化的对象的类。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace FactoryMethodPattern.Implementation2 2 { 3 public class
ConcreteCreator 4 { 5 public virtual AbstractOrInterfaceOfProduct
CreateProduct() 6 { 7 return new ConcreteProduct(); 8 } 9 } 10 11 public
abstract class AbstractOrInterfaceOfProduct 12 { 13 } 14 15 public class
ConcreteProduct : AbstractOrInterfaceOfProduct 16 { 17 } 18 19 public partial
class Test 20 { 21 public void Case2() 22 { 23 ConcreteCreator creator = new
ConcreteCreator(); 24 AbstractOrInterfaceOfProduct product =
creator.CreateProduct(); 25 } 26 } 27 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
**实现方式(三):参数化工厂方法。**
使用参数化工厂方法可以创建多种产品。工厂方法采用一个标识要被创建的对象种类的参数。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace FactoryMethodPattern.Implementation3 2 { 3 public enum
ProductCategory 4 { 5 GoodProduct, 6 BadProduct, 7 } 8 9 public class
ConcreteCreator 10 { 11 public virtual AbstractOrInterfaceOfProduct
CreateProduct(ProductCategory category) 12 { 13 switch (category) 14 { 15 case
ProductCategory.GoodProduct: 16 return new ConcreteGoodProduct(); 17 case
ProductCategory.BadProduct: 18 return new ConcreteBadProduct(); 19 default:20
throw new NotSupportedException(); 21 } 22 } 23 } 24 25 public abstract class
AbstractOrInterfaceOfProduct 26 { 27 } 28 29 public class ConcreteGoodProduct :
AbstractOrInterfaceOfProduct 30 { 31 } 32 33 public class ConcreteBadProduct :
AbstractOrInterfaceOfProduct 34 { 35 } 36 37 public partial class Test 38 { 39
public void Case3() 40 { 41 ConcreteCreator creator = new ConcreteCreator(); 42
AbstractOrInterfaceOfProduct product =
creator.CreateProduct(ProductCategory.GoodProduct); 43 } 44 } 45 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
**实现方式(四):使用模板以避免创建子类。**
使用C\#中的泛型实现工厂方法。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace FactoryMethodPattern.Implementation4 2 { 3 public abstract class
AbstractOrInterfaceOfCreator 4 { 5 public abstract AbstractOrInterfaceOfProduct
CreateProduct(); 6 } 7 8 public class GenericConcreteCreator\<T\> :
AbstractOrInterfaceOfCreator 9 where T : AbstractOrInterfaceOfProduct, new()10 {
11 public AbstractOrInterfaceOfProduct CreateProduct() 12 { 13 return new T();
14 } 15 } 16 17 public abstract class AbstractOrInterfaceOfProduct 18 { 19 } 20
21 public class ConcreteGoodProduct : AbstractOrInterfaceOfProduct 22 { 23 } 24
25 public class ConcreteBadProduct : AbstractOrInterfaceOfProduct 26 { 27 } 28
29 public partial class Test 30 { 31 public void Case3() 32 { 33
AbstractOrInterfaceOfCreator creator1 = new
GenericConcreteCreator\<ConcreteGoodProduct\>(); 34 AbstractOrInterfaceOfCreator
creator2 = new GenericConcreteCreator\<ConcreteBadProduct\>(); 35
AbstractOrInterfaceOfProduct product1 = creator1.CreateProduct(); 36
AbstractOrInterfaceOfProduct product2 = creator2.CreateProduct(); 37 } 38 } 39 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码

View File

@@ -0,0 +1,122 @@
设计模式之美Flyweight享元
**索引**
- 意图
- 结构
- 参与者
- 适用性
- 效果
- 相关模式
- 实现
- 实现方式(一):使用 FlyweightFactory 管理 Flyweight 对象。
**意图**
运用共享技术有效地支持大量细粒度的对象。
Use sharing to support large numbers of fine-grained objects efficiently.
**结构**
![135082566859.png](media/322c8a847832fc5d2385537079343d7c.png)
下面的对象图说明了如何共享 Flyweight
![135430847118.png](media/eaa51861819d82c88eaf50c6924776a9.png)
**参与者**
Flyweight
- 描述一个接口,通过这个接口 Flyweight 可以接受并作用于外部状态。
ConcreteFlyweight
- 实现 Flyweight
接口,并为内部状态增加存储空间。该对象必须是可共享的。它所存储的状态必须是内部的,即必须独立于对象的场景。
UnsharedConcreteFlyweight
- 并非所有的 Flyweight 子类都需要被共享。Flyweight
接口使共享成为可能,但它并不强制共享。
FlyweightFactory
- 创建并管理 Flyweight 对象。
- 确保合理地共享 Flyweight。
Client
- 维持一个对 Flyweight 的引用。
- 计算或存储 Flyweight 的外部状态。
**适用性**
Flyweight 模式的有效性很大程度上取决于如何使用它以及在何处使用它。
当以下情况成立时可以使用 Flyweight 模式:
- 一个应用程序使用了大量的对象。
- 完全由于使用大量对象,造成很大的存储开销。
- 对象的大多数状态都可变为外部状态。
- 如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象。
- 应用程序不依赖于对象标识。
**效果**
- 存储空间上的节省抵消了传输、查找和计算外部状态时的开销。节约量随着共享状态的增多而增大。
**相关模式**
- Flyweight 模式通常和 Composite
模式结合起来,用共享叶节点的又向无环图实现一个逻辑上的层次结构。
- 通常,最好用 Flyweight 实现 State 和 Strategy 对象。
**实现**
**实现方式(一):使用 FlyweightFactory 管理 Flyweight 对象。**
Flyweight
模式的可用性在很大程度上取决于是否易识别外部状态并将它从共享对象中删除。
理想的状况是,外部状态可以由一个单独的对象结构计算得到,且该结构的存储要求非常小。
通常,因为 Flyweight 对象是共享的,用户不能直接对它进行实例化,因为
FlyweightFactory 可以帮助用户查找某个特定的 Flyweight 对象。
共享还意味着某种形式的引用计数和垃圾回收。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace FlyweightPattern.Implementation1 2 { 3 public abstract class
Flyweight 4 { 5 public abstract string Identifier { get; } 6 public abstract
void Operation(string extrinsicState); 7 } 8 9 public class ConcreteFlyweight :
Flyweight 10 { 11 public override string Identifier 12 { 13 get { return
"hello"; }14 } 15 16 public override void Operation(string extrinsicState) 17 {
18 // do something19 } 20 } 21 22 public class FlyweightFactory 23 { 24 private
Dictionary\<string, Flyweight\> \_pool25 = new Dictionary\<string,
Flyweight\>();26 27 public Flyweight CreateFlyweight(string identifier) 28 { 29
if (!\_pool.ContainsKey(identifier)) 30 { 31 Flyweight flyweight = new
ConcreteFlyweight(); 32 \_pool.Add(flyweight.Identifier, flyweight); 33 } 34 35
return \_pool[identifier]; 36 } 37 } 38 39 public class Client 40 { 41 public
void TestCase1() 42 { 43 FlyweightFactory factory = new FlyweightFactory(); 44
Flyweight flyweight1 = factory.CreateFlyweight("hello");45 Flyweight flyweight2
= factory.CreateFlyweight("hello");46 flyweight1.Operation("extrinsic state");47
flyweight2.Operation("extrinsic state");48 } 49 } 50 }

View File

@@ -0,0 +1,198 @@
设计模式之美Interpreter解释器
**索引**
- 意图
- 结构
- 参与者
- 适用性
- 效果
- 相关模式
- 实现
- 实现方式Interpreter 模式结构样式代码。
- 实现方式解释波兰表达式Polish Notation
**意图**
给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
Given a language, define a represention for its grammar along with an
interpreter that uses the representation to interpret sentences in the language.
**结构**
![152516715319.png](media/a6c50fe1fcfff1a376d34e0af97ace54.png)
**参与者**
AbstractExpression
- 声明一个抽象的解释操作,这个接口为抽象语法树中所有的节点所共享。
TerminalExpression
- 实现与文法中的终结符相关联的解释操作。
- 一个句子中的每一个终结符需要该类的一个实例。
NonterminalExpression
- 对文法中的规则的解释操作。
Context
- 包含解释器之外的一些全局信息。
Client
- 构建表示该语法定义的语言中一个特定的句子的抽象语法树。
- 调用解释操作
**适用性**
当有个语言需要解释执行,并且你可将该语言中的句子表示为一个抽象语法树时,可以使用
Interpreter 模式。
当存在以下情况时效果最好:
- 该文法简单对于复杂的文法,文法的类层次变得庞大而无法管理。
- 效率不是一个关键问题,最高效的解释器通常不是通过直接解释语法分析树实现的,而是首先将它们转换成另一种形式。
**效果**
- 易于改变和扩展文法。
- 易于实现文法。
- 复杂的文法难以维护。
- 增加了新的解释表达式的方式。
**相关模式**
- 抽象语法树是一个 Composite 模式的实例。
- 可以使用 Flyweight 模式在抽象语法树中共享终结符。
- 可以使用 Iterator 模式遍历解释器结构。
- 可以使用 Visitor 模式在一个类中维护抽象语法树中的各个节点的行为。
**实现**
**实现方式Interpreter 模式结构样式代码。**
TerminalExpression实现解释 Terminal Symbols 的语法。
NonTerminalExpression聚合一到多个 ExpressionExpression 可以是
TerminalExpression也可以是 NonTerminalExpression。。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace InterpreterPattern.Implementation1 2 { 3 public class Context 4 { 5
public Context(string name) 6 { 7 Name = name; 8 } 9 10 public string Name {
get; private set; }11 } 12 13 public abstract class ExpressionBase 14 { 15
public abstract void Interpret(Context context); 16 } 17 18 public class
TerminalExpression : ExpressionBase 19 { 20 public override void
Interpret(Context context) 21 { 22 Console.WriteLine("Terminal Symbol {0}.",
context.Name);23 } 24 } 25 26 public class NonTerminalExpression :
ExpressionBase 27 { 28 public ExpressionBase Expression1 { get; set; }29 public
ExpressionBase Expression2 { get; set; }30 31 public override void
Interpret(Context context) 32 { 33 Console.WriteLine("Non Terminal Symbol {0}.",
context.Name);34 Expression1.Interpret(context); 35
Expression2.Interpret(context); 36 } 37 } 38 39 public class Client 40 { 41
public void TestCase1() 42 { 43 var context = new Context("Hello World");44 var
root = new NonTerminalExpression 45 { 46 Expression1 = new TerminalExpression(),
47 Expression2 = new TerminalExpression() 48 }; 49 root.Interpret(context); 50 }
51 } 52 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
**实现方式解释波兰表达式Polish Notation**
**中缀表达式**
中缀表达式中,二元运算符总是置于与之相关的两个运算对象之间,根据运算符间的优先关系来确定运算的次序,同时考虑括号规则。
比如: **2 + 3 \* (5 - 1)**
**前缀表达式**
波兰逻辑学家 J.Lukasiewicz 于 1929
年提出了一种不需要括号的表示法将运算符写在运算对象之前也就是前缀表达式即波兰式Polish
Notation, PN
比如:**2 + 3 \* (5 - 1)** 这个表达式的前缀表达式为 **+ 2 \* 3 - 5 1**。
**后缀表达式**
后缀表达式也称为逆波兰式Reverse Polish Notation,
RPN和前缀表达式相反是将运算符号放置于运算对象之后。
比如:**2 + 3 \* (5 - 1)** 用逆波兰式来表示则是:**2 3 5 1 - \* +**。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace InterpreterPattern.Implementation2 2 { 3 public interface
IExpression 4 { 5 int Evaluate(); 6 } 7 8 public class IntegerTerminalExpression
: IExpression 9 { 10 int \_value; 11 12 public IntegerTerminalExpression(int
value) 13 { 14 \_value = value; 15 } 16 17 public int Evaluate() 18 { 19 return
\_value; 20 } 21 22 public override string ToString() 23 { 24 return
\_value.ToString(); 25 } 26 } 27 28 public class AdditionNonterminalExpression :
IExpression 29 { 30 private IExpression \_expr1; 31 private IExpression \_expr2;
32 33 public AdditionNonterminalExpression( 34 IExpression expr1, 35 IExpression
expr2) 36 { 37 \_expr1 = expr1; 38 \_expr2 = expr2; 39 } 40 41 public int
Evaluate() 42 { 43 int value1 = \_expr1.Evaluate(); 44 int value2 =
\_expr2.Evaluate(); 45 return value1 + value2; 46 } 47 48 public override string
ToString() 49 { 50 return string.Format("({0} + {1})", \_expr1, \_expr2); 51 }
52 } 53 54 public class SubtractionNonterminalExpression : IExpression 55 { 56
private IExpression \_expr1; 57 private IExpression \_expr2; 58 59 public
SubtractionNonterminalExpression( 60 IExpression expr1, 61 IExpression expr2) 62
{ 63 \_expr1 = expr1; 64 \_expr2 = expr2; 65 } 66 67 public int Evaluate() 68 {
69 int value1 = \_expr1.Evaluate(); 70 int value2 = \_expr2.Evaluate(); 71
return value1 - value2; 72 } 73 74 public override string ToString() 75 { 76
return string.Format("({0} - {1})", \_expr1, \_expr2); 77 } 78 } 79 80 public
interface IParser 81 { 82 IExpression Parse(string polish); 83 } 84 85 public
class Parser : IParser 86 { 87 public IExpression Parse(string polish) 88 { 89
var symbols = new List\<string\>(polish.Split(' ')); 90 return
ParseNextExpression(symbols); 91 } 92 93 private IExpression
ParseNextExpression(List\<string\> symbols) 94 { 95 int value; 96 if
(int.TryParse(symbols[0], out value)) 97 { 98 symbols.RemoveAt(0); 99 return new
IntegerTerminalExpression(value); 100 } 101 return
ParseNonTerminalExpression(symbols); 102 } 103 104 private IExpression
ParseNonTerminalExpression(List\<string\> symbols)105 { 106 var symbol =
symbols[0];107 symbols.RemoveAt(0);108 109 var expr1 =
ParseNextExpression(symbols); 110 var expr2 = ParseNextExpression(symbols); 111
112 switch (symbol) 113 { 114 case "+":115 return new
AdditionNonterminalExpression(expr1, expr2); 116 case "-":117 return new
SubtractionNonterminalExpression(expr1, expr2); 118 default:119 { 120 string
message = string.Format("Invalid Symbol ({0})", symbol);121 throw new
InvalidOperationException(message); 122 } 123 } 124 } 125 } 126 127 public class
Client 128 { 129 public void TestCase2() 130 { 131 IParser parser = new
Parser(); 132 133 var commands = 134 new string[]135 { 136 "+ 1 2",137 "- 3
4",138 "+ - 5 6 7",139 "+ 8 - 9 1",140 "+ - + - - 2 3 4 + - -5 6 + -7 8 9 0"141
}; 142 143 foreach (var command in commands) 144 { 145 IExpression expression =
parser.Parse(command); 146 Console.WriteLine("{0} = {1}", expression,
expression.Evaluate());147 } 148 149 // Results:150 // (1 + 2) = 3151 // (3 - 4)
= -1152 // ((5 - 6) + 7) = 6153 // (8 + (9 - 1)) = 16154 // (((((2 - 3) - 4) +
((-5 - 6) + (-7 + 8))) - 9) + 0) = -24155 } 156 } 157 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码

View File

@@ -0,0 +1,437 @@
设计模式之美Iterator迭代器
**索引**
- 意图
- 结构
- 参与者
- 适用性
- 效果
- 相关模式
- 实现
- 实现方式Iterator 模式结构样式代码。
- 实现方式(二):实现 IEnumerable 中序遍历二叉树。
- 实现方式(三):实现 BidirectionalConcurrentDictionary 双向并发字典。
- 实现方式(四):实现 RoundRobin 循环列表。
**意图**
提供一种方法顺序访问一个聚合对象中各个元素,而又不需暴露该对象的内部表示。
Provide a way to access the elements of an aggregate object sequentially without
exposing its underlying representation.
**结构**
![207254216383.png](media/2b415d88952f47d7d4cd707cbc009287.png)
**参与者**
Iterator
- 迭代器定义访问和遍历元素的接口。
ConcreteIterator
- 具体迭代器实现迭代器接口。
- 对该聚合遍历时跟踪当前位置。
Aggregate
- 聚合定义创建相应迭代器对象的接口。
ConcreteAggregate
- 具体聚合实现创建相应迭代器的接口,该操作返回 ConreteIterator 的实例。
**适用性**
在以下情况下可以使用 Iterator 模式:
- 访问一个聚合对象的内容而无需暴露它的内部表示。
- 支持对聚合对象的多种遍历。
- 为遍历不同的聚合结构提供一个统一的接口。
**效果**
- 它支持以不同的方式遍历一个聚合。
- 迭代器简化了聚合的接口。
- 在同一个聚合上可以有多个遍历。
**相关模式**
- Iterator 常被应用到 Composite 这样的递归结构上。
- 可以使用 Factory Method 模式来实例化多态迭代器。
- Iterator 可以使用 Memento 来捕获一个迭代的状态,在内部存储 Memento。
**实现**
**实现方式Iterator 模式结构样式代码。**
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace IteratorPattern.Implementation1 2 { 3 public abstract class Iterator
4 { 5 public abstract object First(); 6 public abstract object MoveNext(); 7
public abstract object Current(); 8 public abstract bool IsDone(); 9 public
abstract void Reset(); 10 } 11 12 public abstract class Aggregate 13 { 14 public
abstract Iterator CreateIterator(); 15 } 16 17 public class ConcreteAggregate :
Aggregate 18 { 19 private readonly ArrayList \_items = new ArrayList(); 20 21
public int Count 22 { 23 get { return \_items.Count; } 24 } 25 26 public object
this[int index] 27 { 28 get { return \_items[index]; } 29 set {
\_items.Insert(index, value); } 30 } 31 32 public override Iterator
CreateIterator() 33 { 34 return new ConcreteIterator(this); 35 } 36 } 37 38
public class ConcreteIterator : Iterator 39 { 40 private readonly
ConcreteAggregate \_aggregate; 41 private int \_currentIndex = 0; 42 43 public
ConcreteIterator(ConcreteAggregate aggregate) 44 { 45 \_aggregate = aggregate;
46 } 47 48 public override object First() 49 { 50 if (\_aggregate.Count \> 0) 51
return \_aggregate[0]; 52 else 53 return null; 54 } 55 56 public override object
MoveNext() 57 { 58 object item = null; 59 if (\_currentIndex \<
\_aggregate.Count - 1) 60 { 61 item = \_aggregate[++\_currentIndex]; 62 } 63 64
return item; 65 } 66 67 public override object Current() 68 { 69 return
\_aggregate[\_currentIndex]; 70 } 71 72 public override bool IsDone() 73 { 74
return \_currentIndex \>= \_aggregate.Count; 75 } 76 77 public override void
Reset() 78 { 79 \_currentIndex = 0; 80 } 81 } 82 83 public class Client 84 { 85
public void TestCase1() 86 { 87 var aggregate = new ConcreteAggregate(); 88
aggregate[0] = "Apple"; 89 aggregate[1] = "Orange"; 90 aggregate[2] =
"Strawberry"; 91 92 var iterator = new ConcreteIterator(aggregate); 93 94 object
item = iterator.First(); 95 while (!iterator.IsDone()) 96 { 97
Console.WriteLine(item); 98 item = iterator.MoveNext(); 99 } 100 } 101 } 102 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
**实现方式(二):实现 IEnumerable 中序遍历二叉树。**
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 /// \<summary\> 2 /// 二叉树节点 3 /// \</summary\> 4 /// \<typeparam
name="T"\>The item type\</typeparam\> 5 public class BinaryTreeNode\<T\> 6 { 7
\#region Constructors 8 9 /// \<summary\> 10 /// 二叉树节点 11 /// \</summary\>
12 public BinaryTreeNode() 13 { 14 } 15 16 /// \<summary\> 17 /// 二叉树节点 18
/// \</summary\> 19 /// \<param name="value"\>节点中的值\</param\> 20 public
BinaryTreeNode(T value) 21 { 22 this.Value = value; 23 } 24 25 /// \<summary\>
26 /// 二叉树节点 27 /// \</summary\> 28 /// \<param
name="value"\>节点中的值\</param\> 29 /// \<param
name="parent"\>节点的父节点\</param\> 30 public BinaryTreeNode(T value,
BinaryTreeNode\<T\> parent) 31 { 32 this.Value = value; 33 this.Parent = parent;
34 } 35 36 /// \<summary\> 37 /// 二叉树节点 38 /// \</summary\> 39 /// \<param
name="value"\>节点中的值\</param\> 40 /// \<param
name="parent"\>节点的父节点\</param\> 41 /// \<param
name="left"\>节点的左节点\</param\> 42 /// \<param
name="right"\>节点的右节点\</param\> 43 public BinaryTreeNode(T value, 44
BinaryTreeNode\<T\> parent, 45 BinaryTreeNode\<T\> left, 46 BinaryTreeNode\<T\>
right) 47 { 48 this.Value = value; 49 this.Right = right; 50 this.Left = left;
51 this.Parent = parent; 52 } 53 54 \#endregion 55 56 \#region Properties 57 58
/// \<summary\> 59 /// 节点值 60 /// \</summary\> 61 public T Value { get; set;
} 62 63 /// \<summary\> 64 /// 父节点 65 /// \</summary\> 66 public
BinaryTreeNode\<T\> Parent { get; set; } 67 68 /// \<summary\> 69 /// 左节点 70
/// \</summary\> 71 public BinaryTreeNode\<T\> Left { get; set; } 72 73 ///
\<summary\> 74 /// 右节点 75 /// \</summary\> 76 public BinaryTreeNode\<T\>
Right { get; set; } 77 78 /// \<summary\> 79 /// 是否为根节点 80 ///
\</summary\> 81 public bool IsRoot { get { return Parent == null; } } 82 83 ///
\<summary\> 84 /// 是否为叶子节点 85 /// \</summary\> 86 public bool IsLeaf {
get { return Left == null && Right == null; } } 87 88 /// \<summary\> 89 ///
是否为可访问的 90 /// \</summary\> 91 internal bool Visited { get; set; } 92 93
\#endregion 94 95 \#region Public Overridden Functions 96 97 /// \<summary\> 98
/// Returns a \<see cref="System.String"/\> that represents this instance. 99
/// \</summary\>100 /// \<returns\>101 /// A \<see cref="System.String"/\> that
represents this instance. 102 /// \</returns\>103 public override string
ToString() 104 { 105 return Value.ToString(); 106 } 107 108 \#endregion109 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 /// \<summary\> 2 /// 二叉树 3 /// \</summary\> 4 /// \<typeparam
name="T"\>二叉树中节点内容类型\</typeparam\> 5
[SuppressMessage("Microsoft.Naming",
"CA1710:IdentifiersShouldHaveCorrectSuffix")] 6 public class BinaryTree\<T\> :
ICollection\<T\>, IEnumerable\<T\> where T : IComparable\<T\> 7 { 8 \#region
Constructor 9 10 /// \<summary\> 11 /// 二叉树 12 /// \</summary\> 13 public
BinaryTree() 14 { 15 NumberOfNodes = 0; 16 } 17 18 /// \<summary\> 19 /// 二叉树
20 /// \</summary\> 21 /// \<param name="root"\>二叉树根节点\</param\> 22 public
BinaryTree(BinaryTreeNode\<T\> root) 23 : this() 24 { 25 this.Root = root; 26 }
27 28 \#endregion 29 30 \#region Properties 31 32 /// \<summary\> 33 ///
树的根节点 34 /// \</summary\> 35 public BinaryTreeNode\<T\> Root { get; set; }
36 37 /// \<summary\> 38 /// 树中节点的数量 39 /// \</summary\> 40 protected int
NumberOfNodes { get; set; } 41 42 /// \<summary\> 43 /// 树是否为空 44 ///
\</summary\> 45 public bool IsEmpty { get { return Root == null; } } 46 47 ///
\<summary\> 48 /// 获取树中节点的最小值 49 /// \</summary\> 50 public T MinValue
51 { 52 get 53 { 54 if (IsEmpty) 55 return default(T); 56 57 BinaryTreeNode\<T\>
minNode = Root; 58 while (minNode.Left != null) 59 minNode = minNode.Left; 60 61
return minNode.Value; 62 } 63 } 64 65 /// \<summary\> 66 ///
获取树中节点的最大值 67 /// \</summary\> 68 public T MaxValue 69 { 70 get 71 {
72 if (IsEmpty) 73 return default(T); 74 75 BinaryTreeNode\<T\> maxNode = Root;
76 while (maxNode.Right != null) 77 maxNode = maxNode.Right; 78 79 return
maxNode.Value; 80 } 81 } 82 83 \#endregion 84 85 \#region IEnumerable\<T\>
Members 86 87 /// \<summary\> 88 /// Returns an enumerator that iterates through
the collection. 89 /// \</summary\> 90 /// \<returns\> 91 /// A \<see
cref="T:System.Collections.Generic.IEnumerator\`1"\>\</see\> 92 /// that can be
used to iterate through the collection. 93 /// \</returns\> 94 public
IEnumerator\<T\> GetEnumerator() 95 { 96 foreach (BinaryTreeNode\<T\> node in
Traverse(Root)) 97 { 98 yield return node.Value; 99 } 100 } 101 102
\#endregion103 104 \#region IEnumerable Members 105 106 /// \<summary\>107 ///
Returns an enumerator that iterates through a collection. 108 ///
\</summary\>109 /// \<returns\>110 /// An \<see
cref="T:System.Collections.IEnumerator"/\> 111 /// object that can be used to
iterate through the collection. 112 /// \</returns\>113 IEnumerator
IEnumerable.GetEnumerator() 114 { 115 foreach (BinaryTreeNode\<T\> node in
Traverse(Root)) 116 { 117 yield return node.Value; 118 } 119 } 120 121
\#endregion122 123 \#region ICollection\<T\> Members 124 125 /// \<summary\>126
/// 新增节点 127 /// \</summary\>128 /// \<param name="item"\>The object to add
to the129 /// \<see
cref="T:System.Collections.Generic.ICollection\`1"\>\</see\>.\</param\>130 ///
\<exception cref="T:System.NotSupportedException"\>The131 /// \<see
cref="T:System.Collections.Generic.ICollection\`1"\>\</see\> 132 /// is
read-only.\</exception\>133 public void Add(T item) 134 { 135 if (Root ==
null)136 { 137 Root = new BinaryTreeNode\<T\>(item); 138 ++NumberOfNodes; 139 }
140 else141 { 142 Insert(item); 143 } 144 } 145 146 /// \<summary\>147 ///
清除树 148 /// \</summary\>149 public void Clear() 150 { 151 Root = null;152 }
153 154 /// \<summary\>155 /// 树中是否包含此节点 156 /// \</summary\>157 ///
\<param name="item"\>The object to locate in the158 /// \<see
cref="T:System.Collections.Generic.ICollection\`1"\>\</see\>.\</param\>159 ///
\<returns\>160 /// true if item is found in the 161 /// \<see
cref="T:System.Collections.Generic.ICollection\`1"\>\</see\>; otherwise,
false.162 /// \</returns\>163 public bool Contains(T item) 164 { 165 if
(IsEmpty) 166 return false;167 168 BinaryTreeNode\<T\> currentNode = Root; 169
while (currentNode != null)170 { 171 int comparedValue =
currentNode.Value.CompareTo(item); 172 if (comparedValue == 0)173 return
true;174 else if (comparedValue \< 0)175 currentNode = currentNode.Left; 176
else177 currentNode = currentNode.Right; 178 } 179 180 return false;181 } 182
183 /// \<summary\>184 /// 将树中节点拷贝至目标数组 185 /// \</summary\>186 ///
\<param name="array"\>The array.\</param\>187 /// \<param
name="arrayIndex"\>Index of the array.\</param\>188 public void CopyTo(T[]
array, int arrayIndex) 189 { 190 T[] tempArray = new T[NumberOfNodes]; 191 int
counter = 0;192 foreach (T value in this)193 { 194 tempArray[counter] = value;
195 ++counter; 196 } 197 Array.Copy(tempArray, 0, array, arrayIndex, Count);198
} 199 200 /// \<summary\>201 /// 树中节点的数量 202 /// \</summary\>203 public
int Count 204 { 205 get { return NumberOfNodes; } 206 } 207 208 ///
\<summary\>209 /// 树是否为只读 210 /// \</summary\>211 public bool IsReadOnly
212 { 213 get { return false; }214 } 215 216 /// \<summary\>217 /// 移除节点 218
/// \</summary\>219 /// \<param name="item"\>节点值\</param\>220 ///
\<returns\>是否移除成功\</returns\>221 public bool Remove(T item) 222 { 223
BinaryTreeNode\<T\> node = Find(item); 224 if (node == null)225 return false;226
227 List\<T\> values = new List\<T\>(); 228 foreach (BinaryTreeNode\<T\> l in
Traverse(node.Left)) 229 { 230 values.Add(l.Value); 231 } 232 foreach
(BinaryTreeNode\<T\> r in Traverse(node.Right)) 233 { 234 values.Add(r.Value);
235 } 236 237 if (node.Parent.Left == node) 238 { 239 node.Parent.Left =
null;240 } 241 else242 { 243 node.Parent.Right = null;244 } 245 246 node.Parent
= null;247 248 foreach (T v in values) 249 { 250 this.Add(v);251 } 252 253
return true;254 } 255 256 \#endregion257 258 \#region Private Functions 259 260
/// \<summary\>261 /// 查找指定值的节点 262 /// \</summary\>263 /// \<param
name="value"\>指定值\</param\>264 /// \<returns\>265 /// 指定值的节点 266 ///
\</returns\>267 protected BinaryTreeNode\<T\> Find(T value) 268 { 269 foreach
(BinaryTreeNode\<T\> node in Traverse(Root)) 270 if (node.Value.Equals(value))
271 return node; 272 return null;273 } 274 275 /// \<summary\>276 /// 遍历树 277
/// \</summary\>278 /// \<param name="node"\>遍历搜索的起始节点\</param\>279 ///
\<returns\>280 /// The individual items from the tree 281 /// \</returns\>282
[SuppressMessage("Microsoft.Design",
"CA1006:DoNotNestGenericTypesInMemberSignatures")]283 protected
IEnumerable\<BinaryTreeNode\<T\>\> Traverse(BinaryTreeNode\<T\> node) 284 { 285
// 遍历左子树286 if (node.Left != null)287 { 288 foreach (BinaryTreeNode\<T\>
left in Traverse(node.Left)) 289 yield return left; 290 } 291 292 //
中序遍历二叉树, 顺序是 左子树右子树293 yield return node; 294 295 //
遍历右子树296 if (node.Right != null)297 { 298 foreach (BinaryTreeNode\<T\>
right in Traverse(node.Right)) 299 yield return right; 300 } 301 } 302 303 ///
\<summary\>304 /// 插入节点 305 /// \</summary\>306 /// \<param
name="value"\>插入的节点值\</param\>307 protected void Insert(T value) 308 { 309
// 从根节点开始比较310 BinaryTreeNode\<T\> currentNode = Root; 311 312 while
(true)313 { 314 if (currentNode == null)315 throw new
InvalidProgramException("The current tree node cannot be null.");316 317 //
比较当前节点与新节点的值318 int comparedValue =
currentNode.Value.CompareTo(value); 319 if (comparedValue \< 0)320 { 321 //
当前节点值小于新节点值322 if (currentNode.Left == null)323 { 324
currentNode.Left = new BinaryTreeNode\<T\>(value, currentNode); 325
\++NumberOfNodes; 326 return;327 } 328 else329 { 330 currentNode =
currentNode.Left; 331 } 332 } 333 else if (comparedValue \> 0)334 { 335 //
当前节点值大于新节点值336 if (currentNode.Right == null)337 { 338
currentNode.Right = new BinaryTreeNode\<T\>(value, currentNode); 339
\++NumberOfNodes; 340 return;341 } 342 else343 { 344 currentNode =
currentNode.Right; 345 } 346 } 347 else348 { 349 // 当前节点值等于新节点值350
currentNode = currentNode.Right; 351 } 352 } 353 } 354 355 \#endregion356 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
**实现方式(三):实现 BidirectionalConcurrentDictionary 双向并发字典。**
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace IteratorPattern.Implementation3 2 { 3 /// \<summary\> 4 /// 双值对 5
/// \</summary\> 6 /// \<typeparam name="TFirst"\>第一个值的类型\</typeparam\> 7
/// \<typeparam name="TSecond"\>第二个值的类型\</typeparam\> 8 [Serializable] 9
public struct FirstSecondPair\<TFirst, TSecond\> 10 { 11 private TFirst first;
12 private TSecond second; 13 14 /// \<summary\> 15 /// 第一个值 16 ///
\</summary\> 17 public TFirst First 18 { 19 get 20 { 21 return this.first; 22 }
23 } 24 25 /// \<summary\> 26 /// 第二个值 27 /// \</summary\> 28 public TSecond
Second 29 { 30 get 31 { 32 return this.second; 33 } 34 } 35 36 /// \<summary\>
37 /// 双值对 38 /// \</summary\> 39 /// \<param
name="first"\>第一个值\</param\> 40 /// \<param
name="second"\>第二个值\</param\> 41 public FirstSecondPair(TFirst first,
TSecond second) 42 { 43 if (first == null) 44 throw new
ArgumentNullException("first"); 45 if (second == null) 46 throw new
ArgumentNullException("second"); 47 48 this.first = first; 49 this.second =
second; 50 } 51 52 /// \<summary\> 53 /// Determines whether the specified \<see
cref="System.Object"/\> is equal to this instance. 54 /// \</summary\> 55 ///
\<param name="obj"\>The \<see cref="System.Object"/\> to compare with this
instance.\</param\> 56 /// \<returns\> 57 /// \<c\>true\</c\> if the specified
\<see cref="System.Object"/\> is equal to this instance; otherwise,
\<c\>false\</c\>. 58 /// \</returns\> 59 public override bool Equals(object obj)
60 { 61 if (obj == null) 62 return false; 63 64 FirstSecondPair\<TFirst,
TSecond\> target = (FirstSecondPair\<TFirst, TSecond\>)obj; 65 return
this.First.Equals(target.First) && this.Second.Equals(target.Second); 66 } 67 68
/// \<summary\> 69 /// Returns a hash code for this instance. 70 ///
\</summary\> 71 /// \<returns\> 72 /// A hash code for this instance, suitable
for use in hashing algorithms and data structures like a hash table. 73 ///
\</returns\> 74 public override int GetHashCode() 75 { 76 return
base.GetHashCode(); 77 } 78 79 /// \<summary\> 80 /// Returns a \<see
cref="System.String"/\> that represents this instance. 81 /// \</summary\> 82
/// \<returns\> 83 /// A \<see cref="System.String"/\> that represents this
instance. 84 /// \</returns\> 85 public override string ToString() 86 { 87
StringBuilder sb = new StringBuilder(); 88 sb.Append('['); 89 90 if (this.First
!= null) 91 { 92 sb.Append(this.First.ToString()); 93 } 94 95 sb.Append(", ");
96 97 if (this.Second != null) 98 { 99 sb.Append(this.Second.ToString());100 }
101 102 sb.Append(']');103 104 return sb.ToString(); 105 } 106 107 ///
\<summary\>108 /// Implements the operator ==. 109 /// \</summary\>110 ///
\<param name="left"\>The left.\</param\>111 /// \<param name="right"\>The
right.\</param\>112 /// \<returns\>113 /// The result of the operator. 114 ///
\</returns\>115 public static bool operator ==(FirstSecondPair\<TFirst,
TSecond\> left, FirstSecondPair\<TFirst, TSecond\> right) 116 { 117 if
(((object)left == null) \|\| ((object)right == null))118 { 119 return false;120
} 121 122 return left.Equals(right); 123 } 124 125 /// \<summary\>126 ///
Implements the operator !=. 127 /// \</summary\>128 /// \<param name="left"\>The
left.\</param\>129 /// \<param name="right"\>The right.\</param\>130 ///
\<returns\>131 /// The result of the operator. 132 /// \</returns\>133 public
static bool operator !=(FirstSecondPair\<TFirst, TSecond\> left,
FirstSecondPair\<TFirst, TSecond\> right) 134 { 135 return !(left == right); 136
} 137 } 138 139 public class BidirectionalConcurrentDictionary\<TFirst,
TSecond\> : IEnumerable\<FirstSecondPair\<TFirst, TSecond\>\> 140 { 141 \#region
Fields 142 143 private ConcurrentDictionary\<TFirst, TSecond\> firstToSecond =
new ConcurrentDictionary\<TFirst, TSecond\>(); 144 private
ConcurrentDictionary\<TSecond, TFirst\> secondToFirst = new
ConcurrentDictionary\<TSecond, TFirst\>(); 145 146 \#endregion147 148 \#region
Public Methods 149 150 public void Add(TFirst first, TSecond second) 151 { 152
if (firstToSecond.ContainsKey(first) \|\| secondToFirst.ContainsKey(second)) 153
throw new ArgumentException("Duplicate first or second");154 155
firstToSecond.Add(first, second); 156 secondToFirst.Add(second, first); 157 }
158 159 public bool ContainsFirst(TFirst first) 160 { 161 return
firstToSecond.ContainsKey(first); 162 } 163 164 public bool
ContainsSecond(TSecond second) 165 { 166 return
secondToFirst.ContainsKey(second); 167 } 168 169 public TSecond
GetByFirst(TFirst first) 170 { 171 TSecond second; 172 if
(!firstToSecond.TryGetValue(first, out second)) 173 throw new
KeyNotFoundException("Cannot find second by first.");174 175 return second; 176
} 177 178 public TFirst GetBySecond(TSecond second) 179 { 180 TFirst first; 181
if (!secondToFirst.TryGetValue(second, out first)) 182 throw new
KeyNotFoundException("Cannot find first by second.");183 184 return first; 185 }
186 187 public void RemoveByFirst(TFirst first) 188 { 189 TSecond second; 190 if
(!firstToSecond.TryGetValue(first, out second)) 191 throw new
KeyNotFoundException("Cannot find second by first.");192 193
firstToSecond.Remove(first); 194 secondToFirst.Remove(second); 195 } 196 197
public void RemoveBySecond(TSecond second) 198 { 199 TFirst first; 200 if
(!secondToFirst.TryGetValue(second, out first)) 201 throw new
KeyNotFoundException("Cannot find first by second.");202 203
secondToFirst.Remove(second); 204 firstToSecond.Remove(first); 205 } 206 207
public bool TryAdd(TFirst first, TSecond second) 208 { 209 if
(firstToSecond.ContainsKey(first) \|\| secondToFirst.ContainsKey(second)) 210
return false;211 212 firstToSecond.Add(first, second); 213
secondToFirst.Add(second, first); 214 return true;215 } 216 217 public bool
TryGetByFirst(TFirst first, out TSecond second) 218 { 219 return
firstToSecond.TryGetValue(first, out second); 220 } 221 222 public bool
TryGetBySecond(TSecond second, out TFirst first) 223 { 224 return
secondToFirst.TryGetValue(second, out first); 225 } 226 227 public bool
TryRemoveByFirst(TFirst first) 228 { 229 TSecond second; 230 if
(!firstToSecond.TryGetValue(first, out second)) 231 return false;232 233
firstToSecond.Remove(first); 234 secondToFirst.Remove(second); 235 return
true;236 } 237 238 public bool TryRemoveBySecond(TSecond second) 239 { 240
TFirst first; 241 if (!secondToFirst.TryGetValue(second, out first)) 242 return
false;243 244 secondToFirst.Remove(second); 245 firstToSecond.Remove(first); 246
return true;247 } 248 249 public int Count 250 { 251 get { return
firstToSecond.Count; } 252 } 253 254 public void Clear() 255 { 256
firstToSecond.Clear(); 257 secondToFirst.Clear(); 258 } 259 260 \#endregion261
262 \#region IEnumerable\<FirstSecondPair\<TFirst,TSecond\>\> Members 263 264
IEnumerator\<FirstSecondPair\<TFirst, TSecond\>\>
IEnumerable\<FirstSecondPair\<TFirst, TSecond\>\>.GetEnumerator() 265 { 266
foreach (var item in firstToSecond) 267 { 268 yield return new
FirstSecondPair\<TFirst, TSecond\>(item.Key, item.Value); 269 } 270 } 271 272
\#endregion273 274 \#region IEnumerable Members 275 276 IEnumerator
IEnumerable.GetEnumerator() 277 { 278 foreach (var item in firstToSecond) 279 {
280 yield return new FirstSecondPair\<TFirst, TSecond\>(item.Key, item.Value);
281 } 282 } 283 284 \#endregion285 } 286 287 public static class
ConcurrentDictionaryExtensions 288 { 289 public static TValue Add\<TKey,
TValue\>(this ConcurrentDictionary\<TKey, TValue\> collection, TKey key, TValue
@value) 290 { 291 TValue result = collection.AddOrUpdate(key, @value, (k, v) =\>
{ return @value; }); 292 return result; 293 } 294 295 public static TValue
Update\<TKey, TValue\>(this ConcurrentDictionary\<TKey, TValue\> collection,
TKey key, TValue @value) 296 { 297 TValue result = collection.AddOrUpdate(key,
@value, (k, v) =\> { return @value; }); 298 return result; 299 } 300 301 public
static TValue Get\<TKey, TValue\>(this ConcurrentDictionary\<TKey, TValue\>
collection, TKey key) 302 { 303 TValue @value = default(TValue);304
collection.TryGetValue(key, out @value); 305 return @value; 306 } 307 308 public
static TValue Remove\<TKey, TValue\>(this ConcurrentDictionary\<TKey, TValue\>
collection, TKey key) 309 { 310 TValue @value = default(TValue);311
collection.TryRemove(key, out @value); 312 return @value; 313 } 314 } 315 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
**实现方式(四):实现 RoundRobin 循环列表。**
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace IteratorPattern.Implementation4 2 { 3 /// \<summary\> 4 /// 循环列表
5 /// \</summary\> 6 /// \<typeparam name="T"\>\</typeparam\> 7 public class
RoundRobinCollection\<T\> : IEnumerable\<T\> 8 { 9 private T[] \_items; 10
private int \_head; 11 12 /// \<summary\>13 /// 循环列表 14 /// \</summary\>15
/// \<param name="items"\>供循环的列表项\</param\>16 public
RoundRobinCollection(IEnumerable\<T\> items) 17 { 18 if (items == null \|\|
items.Count\<T\>() == 0)19 { 20 throw new ArgumentException( 21 "One or more
items must be provided", "items");22 } 23 24 // copy the list to ensure it
doesn't change on us25 // (and so we can lock() on our private copy) 26 \_items
= items.ToArray(); 27 } 28 29 /// \<summary\>30 /// 获取循环器 31 ///
\</summary\>32 /// \<returns\>\</returns\>33 public IEnumerator\<T\>
GetEnumerator() 34 { 35 int currentHead; 36 37 lock (_items) 38 { 39 currentHead
= \_head++; 40 41 if (_head == \_items.Length) 42 { 43 // wrap back to the start
44 \_head = 0;45 } 46 } 47 48 // return results [current] ... [last] 49 for (int
i = currentHead; i \< \_items.Length; i++) 50 { 51 yield return \_items[i]; 52 }
53 54 // return wrap-around (if any) [0] ... [current-1] 55 for (int i = 0; i \<
currentHead; i++)56 { 57 yield return \_items[i]; 58 } 59 } 60 61 ///
\<summary\>62 /// 获取循环器 63 /// \</summary\>64 /// \<returns\>\</returns\>65
IEnumerator IEnumerable.GetEnumerator() 66 { 67 return this.GetEnumerator();68 }
69 } 70 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码

View File

@@ -0,0 +1,105 @@
**索引**
- 意图
- 结构
- 参与者
- 适用性
- 效果
- 实现
- 实现方式Manager 模式的示例实现。
**意图**
将对一个类的所有对象的管理封装到一个单独的管理器类中。
这使得管理职责的变化独立于类本身,并且管理器还可以为不同的类进行重用。
Encapsulates management of a classs objects into a separate manager object.
This allows variation of management functionality independent of the class and
the managers reuse for different classes.
**结构**
![110459515618.png](media/7428e3ffa507ae62c5f8f131247eba36.png)
**参与者**
Subject
- 领域对象。
- 提供 Client 需要的领域服务。
Manager
- Manager 类是唯一负责创建和销毁 Subject 对象的类。它负责跟踪和管理 Subject
对象。
- 典型的管理职责包括根据指定的 Key 搜索 Subject 对象。
- 因为 Subject 对 Manager 无引用,所以 Manager 可根据需要修改或子类化。
Client
- 从 Manager 对象获取 Subject 对象。
- 使用 Subject 的领域服务。
![116396859253.png](media/5f82f2984b309bf829857bedadd73e7d.png)
**适用性**
当以下情况成立时可以使用 Manager 模式:
- 当需要对同一个类的所有的对象进行操作时。
- 当需要按需的创建和销毁对象时。
- 当需要控制对象的生命周期时。
**效果**
- 可以对全部对象进行统计。
- 管理职责可以无依赖的变化。
- 可以按需替换管理职责。
- 管理职责可以得到重用
**实现**
**实现方式Manager 模式的示例实现。**
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace ManagerPattern.Implementation1 2 { 3 public class Book 4 { 5 public
Book(string isbn, string authors, string title) 6 { 7 this.ISBN = isbn; 8
this.Authors = authors; 9 this.Title = title;10 } 11 12 public string ISBN {
get; private set; }13 public string Authors { get; private set; }14 public
string Title { get; private set; }15 16 public string Publisher { get; set; }17
public Image Cover { get; set; }18 19 public string GetTableOfContents() 20 { 21
return "something";22 } 23 } 24 25 public class BookManager 26 { 27 private
Dictionary\<string, Book\> \_books28 = new Dictionary\<string, Book\>();29 30
public BookManager() 31 { 32 } 33 34 public Book AddBook(string isbn, string
authors, string title) 35 { 36 Book book = new Book(isbn, authors, title); 37
\_books.Add(book.ISBN, book); 38 return book; 39 } 40 41 public Book
GetBookByISBN(string isbn) 42 { 43 Book book; 44 \_books.TryGetValue(isbn, out
book); 45 return book; 46 } 47 48 public IEnumerable\<Book\>
FindBooksOfAuthor(string author) 49 { 50 return \_books.Values.Where(b =\>
b.Authors.Contains(author)); 51 } 52 } 53 54 public class Client 55 { 56 public
void TestCase1() 57 { 58 BookManager manager = new BookManager(); 59
manager.AddBook("xxxx-xxxx-xxxx", "Dennis Gao", "Good Man");60 Book book =
manager.GetBookByISBN("xxxx-xxxx-xxxx");61 book.GetTableOfContents(); 62 } 63 }
64 }
复制代码

View File

@@ -0,0 +1,131 @@
设计模式之美Mediator中介者
**索引**
- 意图
- 结构
- 参与者
- 适用性
- 效果
- 相关模式
- 实现
- 实现方式Mediator 模式结构样式代码。
**意图**
用一个中介对象来封装一系列的对象交互。
中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
Define an object that encapsulates how a set of objects interact.
Mediator promotes loose coupling by keeping objects from referring to each other
explicitly, and it lets you vary their interaction independently.
**结构**
![217545939801.png](media/7425d22312c40949be9309f07a4f5078.png)
一个典型的对象结构:
**参与者**
Mediator
- 中介者定义一个接口用于与各同事对象通信。
ConcreteMediator
- 具体中介者通过协调各同事对象实现协作行为。
- 了解并维护它的各个同事。
Colleague
- 每一个同事类都知道它的中介者。
- 每一个同事对象在需与其他同事通信时,与它的中介者通信。
**适用性**
在以下情况下可以使用 Mediator 模式:
- 一组对象定义良好但是使用复杂的通信方式。产生的相互依赖关系结构混乱且难以理解。
- 一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用该对象。
- 想定制一个分布在多个类中的行为,而又不想生成太多的子类。
**效果**
- 减少了子类的生成。
- 将各个同事类解耦。
- 它简化了对象协议。
- 它对对象如何协作进行了抽象。
- 它使控制集中化。
**相关模式**
- Facade 模式与 Mediator
模式的不同之处在于它是对一个对象子系统进行抽象,从而提供了一个更为方便的接口。它的协议是单向的,即
Facade 对象对这个子系统类提出要求但反之则不行。Mediator 提供了各 Colleague
对象不支持或不能支持的协作行为,而协议是多向的。
- Colleague 可以使用 Observer 模式与 Mediator 通信。
**实现**
**实现方式Mediator 模式结构样式代码。**
Colleague-Mediator 的通信中当一个感兴趣的事件发生时Colleague 必须与其
Mediator 通信。
一种方式是使用 Observer 模式,将 Mediator 实现为一个 Observer各 Colleague 作为
Subject。
另一种方式是在 Mediator 中定义一个特殊的通知接口,各 Colleague
在通信时直接调用该接口。
复制代码
1 namespace MediatorPattern.Implementation1 2 { 3 public abstract class
Colleague 4 { 5 protected Mediator \_mediator; 6 7 public Colleague(Mediator
mediator) 8 { 9 \_mediator = mediator; 10 } 11 12 public abstract void
Send(string message); 13 public abstract void Notify(string message); 14 } 15 16
public abstract class Mediator 17 { 18 public abstract void
SendMessage(Colleague sender, string message); 19 } 20 21 public class
ConcreteMediator : Mediator 22 { 23 private ConcreteColleague1 \_colleague1; 24
private ConcreteColleague2 \_colleague2; 25 26 public ConcreteColleague1
Colleague1 27 { 28 set { \_colleague1 = value; } 29 } 30 31 public
ConcreteColleague2 Colleague2 32 { 33 set { \_colleague2 = value; } 34 } 35 36
public override void SendMessage(Colleague sender, string message) 37 { 38 if
(sender == \_colleague1) 39 { 40 \_colleague2.Notify(message); 41 } 42 else if
(sender == \_colleague2) 43 { 44 \_colleague1.Notify(message); 45 } 46 } 47 } 48
49 public class ConcreteColleague1 : Colleague 50 { 51 public
ConcreteColleague1(Mediator mediator) 52 : base(mediator) 53 { 54 } 55 56 public
override void Send(string message) 57 { 58 \_mediator.SendMessage(this,
message); 59 } 60 61 public override void Notify(string message) 62 { 63
Console.WriteLine("Colleague1 gets message: " + message); 64 } 65 } 66 67 public
class ConcreteColleague2 : Colleague 68 { 69 public ConcreteColleague2(Mediator
mediator) 70 : base(mediator) 71 { 72 } 73 74 public override void Send(string
message) 75 { 76 \_mediator.SendMessage(this, message); 77 } 78 79 public
override void Notify(string message) 80 { 81 Console.WriteLine("Colleague2 gets
message: " + message); 82 } 83 } 84 85 public class Client 86 { 87 public void
TestCase1() 88 { 89 var mediator = new ConcreteMediator(); 90 91 var colleague1
= new ConcreteColleague1(mediator); 92 var colleague2 = new
ConcreteColleague2(mediator); 93 94 mediator.Colleague1 = colleague1; 95
mediator.Colleague2 = colleague2; 96 97 colleague1.Send("How are you?"); 98
colleague2.Send("Fine, Thank you!"); 99 } 100 } 101 }
复制代码

View File

@@ -0,0 +1,115 @@
设计模式之美Memento备忘录
**索引**
- 意图
- 结构
- 参与者
- 适用性
- 效果
- 相关模式
- 实现
- 实现方式Memento 模式结构样式代码。
**别名**
- Token
**意图**
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
Without violating encapsulation, capture and externalize an object's internal
state so that the object can be restored to this state later.
**结构**
![229273589556.png](media/5870f1e66b4dba9e3f59dcd29cf0f8bd.png)
**参与者**
Memento
- Memento 存储 Originator 对象的内部状态。Originator 根据需要决定 Memento
存储那些内部状态。
- 防止 Originator 以外的其他对象访问 Memento。Memento
可实现两个接口Caretaker 只能看到 Memento 的窄接口Originator
可以看到宽接口。
Originator
- Originator 创建一个 Memento用以记录当前时刻的内部状态。
- 使用 Memento 恢复内部状态。
Caretaker
- 负责保存 Memento。
- 不能对 Memento 的内容进行操作和检查。
**适用性**
在以下情况下可以使用 Memento 模式:
- 必须保存一个对象在某一个时刻的状态,这样以后需要时它才能恢复到先前的状态。
- 如果一个用接口来让其他对象直接得到的这些状态,将会暴露对象的实现细节并破坏对象的封装性。
**效果**
- 保持封装边界。
- 简化了 Originator。
- 定义窄接口和宽接口。
- 使用和维护 Memento 的潜在代价。
**相关模式**
- 可以使用 Memento 存储 Command 的内部状态,以支持撤销操作。
- Memento 可以使用 Iterator 进行迭代。
**实现**
Caretaker 向 Originator 请求一个 Memento保留一段时间后将其送回 Originator。
![234331402873.png](media/9e12cba8d2bed321f8e089e839e244ad.png)
**实现方式Memento 模式结构样式代码。**
Memento 有两个接口:一个为 Originator
所使用的宽接口,一个为其他对象所使用的窄接口。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace MementoPattern.Implementation1 2 { 3 public class Memento 4 { 5
private readonly string \_state; 6 7 public Memento(string state) 8 { 9 \_state
= state; 10 } 11 12 public string GetState() 13 { 14 return \_state; 15 } 16 }
17 18 public class Originator 19 { 20 public string State { get; set; }21 22
public Memento CreateMemento() 23 { 24 return (new Memento(State)); 25 } 26 27
public void SetMemento(Memento memento) 28 { 29 State = memento.GetState(); 30 }
31 } 32 33 public class Caretaker 34 { 35 public Memento Memento { get; set; }36
} 37 38 public class Client 39 { 40 public void TestCase1() 41 { 42 var
originator = new Originator { State = "State A" }; 43
Console.WriteLine(originator.State); 44 45 var memento =
originator.CreateMemento(); 46 var caretaker = new Caretaker { Memento = memento
}; 47 48 originator.State = "State B";49 Console.WriteLine(originator.State); 50
51 originator.SetMemento(caretaker.Memento); 52
Console.WriteLine(originator.State); 53 } 54 } 55 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码

View File

@@ -0,0 +1,104 @@
**索引**
- 意图
- 结构
- 参与者
- 适用性
- 效果
- 相关模式
- 实现
- 实现方式Null Object 的示例实现。
**意图**
通过对缺失对象的封装,以提供默认无任何行为的对象替代品。
Encapsulate the absence of an object by providing a substitutable alternative
that offers suitable default do nothing behavior.
In short, a design where "nothing will come of nothing".
**结构**
![352014475871.png](media/449673a26c6c7fbe0fe391c24734c75a.png)
**参与者**
AbstractObject
- 声明协作对象的接口。
- 如果需要,可以实现默认行为。
RealObject
- 具体的协作对象类,提供有意义的行为。
NullObject
- 空对象类,继承自 AbstractObject但接口实现不做任何事情。
Client
- 请求协作对象。
**适用性**
当以下情况成立时可以使用 Null Object 模式:
- 一个对象需要一个协作对象,但并无具体的协作对象。
- 协作对象不需要做任何事情。
**效果**
- 减少了对对象是否为 Null 的判断。
- 提供默认无任何具体行为的协作对象。
**相关模式**
- 因为 Null Object 通常没有任何状态,所以多个实例可能都是类似的,可以使用
[Singleton](http://www.cnblogs.com/gaochundong/p/design_pattern_singleton.html)
模式来实现。
- Null Object 看起来很像
[Proxy](http://www.cnblogs.com/gaochundong/p/design_pattern_proxy.html)
模式但两者有着不同的目的。Proxy 提供对目标对象访问的控制,而 Null Object
并不隐藏任何对象。
- Null Object 可以作为
[Strategy](http://www.cnblogs.com/gaochundong/p/design_pattern_strategy.html)
模式中的一个特例。Strategy 提供多个具体类以区分算法,而 NullStrategy
则可不做任何事情。
- Null Object 可以作为
[State](http://www.cnblogs.com/gaochundong/p/design_pattern_state.html)
模式中的一个特例。使用 NullState 以便不做任何事情。
-
[Visitor](http://www.cnblogs.com/gaochundong/p/design_pattern_visitor.html)
模式中也可以使用 Null Object 以提供某种默认行为。
**实现**
**实现方式Null Object 的示例实现。**
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace NullObjectPattern.Implementation1 2 { 3 public interface ILog 4 { 5
void Write(string message); 6 } 7 8 public class ConsoleLog : ILog 9 { 10 public
void Write(string message) 11 { 12 Console.WriteLine(message); 13 } 14 } 15 16
public class NullLog : ILog 17 { 18 public void Write(string message) 19 { 20 //
do nothing21 } 22 } 23 24 public class Client 25 { 26 public void TestCase1() 27
{ 28 ILog log1 = new ConsoleLog(); 29 ILog log2 = new NullLog(); 30 31
log1.Write("message to log");32 log2.Write("message to log");33 } 34 } 35 }

View File

@@ -0,0 +1,161 @@
**索引**
- 意图
- 结构
- 参与者
- 适用性
- 效果
- 相关模式
- 实现
- 实现方式(一):实现 DatabaseConnectionPool 类。
- 实现方式(二):使用对象构造方法和预分配方式实现 ObjectPool 类。
**意图**
运用对象池化技术可以显著地提升性能,尤其是当对象的初始化过程代价较大或者频率较高时。
Object pooling can offer a significant performance boost; it is most effective
in situations where the cost of initializing a class instance is high, the rate
of instantiation of a class is high.
**结构**
![724507919518.png](media/e2021eb969b6e2e1521577e9d395b462.png)
**参与者**
Reusable
- 类的实例与其他对象进行有限时间的交互。
ReusablePool
- 管理类的实例。
Client
- 使用类的实例。
**适用性**
当以下情况成立时可以使用 Object Pool 模式:
- 类的实例可重用于交互。
- 类的实例化过程开销较大。
- 类的实例化的频率较高。
- 类参与交互的时间周期有限。
**效果**
- 节省了创建类的实例的开销。
- 节省了创建类的实例的时间。
- 存储空间随着对象的增多而增大。
**相关模式**
- 通常,可以使用 Singleton 模式实现 ReusablePool 类。
- Factory Method 模式封装了对象的创建的过程但其不负责管理对象。Object Pool
负责管理对象。
**实现**
**实现方式(一):实现 DatabaseConnectionPool 类。**
如果 Client 调用 ObjectPool 的 AcquireReusable() 方法来获取 Reusable 对象,当在
ObjectPool 中存在可用的 Reusable 对象时,其将一个 Reusable
从池中移除,然后返回该对象。如果池为空,则 ObjectPool 会创建一个新的 Reusable
对象。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace ObjectPoolPattern.Implementation1 2 { 3 public abstract class
ObjectPool\<T\> 4 { 5 private TimeSpan \_expirationTime; 6 private
Dictionary\<T, DateTime\> \_unlocked; 7 private Dictionary\<T, DateTime\>
\_locked; 8 private readonly object \_sync = new object(); 9 10 public
ObjectPool() 11 { 12 \_expirationTime = TimeSpan.FromSeconds(30); 13 \_locked =
new Dictionary\<T, DateTime\>(); 14 \_unlocked = new Dictionary\<T,
DateTime\>(); 15 } 16 17 public ObjectPool(TimeSpan expirationTime) 18 : this()
19 { 20 \_expirationTime = expirationTime; 21 } 22 23 protected abstract T
Create(); 24 25 public abstract bool Validate(T reusable); 26 27 public abstract
void Expire(T reusable); 28 29 public T CheckOut() 30 { 31 lock (_sync) 32 { 33
T reusable = default(T); 34 35 if (\_unlocked.Count \> 0) 36 { 37 foreach (var
item in \_unlocked) 38 { 39 if ((DateTime.UtcNow - item.Value) \>
\_expirationTime) 40 { 41 // object has expired 42 \_unlocked.Remove(item.Key);
43 Expire(item.Key); 44 } 45 else 46 { 47 if (Validate(item.Key)) 48 { 49 //
find a reusable object 50 \_unlocked.Remove(item.Key); 51 \_locked.Add(item.Key,
DateTime.UtcNow); 52 reusable = item.Key; 53 break; 54 } 55 else 56 { 57 //
object failed validation 58 \_unlocked.Remove(item.Key); 59 Expire(item.Key); 60
} 61 } 62 } 63 } 64 65 // no object available, create a new one 66 if (reusable
== null) 67 { 68 reusable = Create(); 69 \_locked.Add(reusable,
DateTime.UtcNow); 70 } 71 72 return reusable; 73 } 74 } 75 76 public void
CheckIn(T reusable) 77 { 78 lock (_sync) 79 { 80 \_locked.Remove(reusable); 81
\_unlocked.Add(reusable, DateTime.UtcNow); 82 } 83 } 84 } 85 86 public class
DatabaseConnection : IDisposable 87 { 88 // do some heavy works 89 public
DatabaseConnection(string connectionString) 90 { 91 } 92 93 public bool IsOpen {
get; set; } 94 95 // release something 96 public void Dispose() 97 { 98 } 99 }
100 101 public class DatabaseConnectionPool : ObjectPool\<DatabaseConnection\>
102 { 103 private string \_connectionString; 104 105 public
DatabaseConnectionPool(string connectionString) 106 :
base(TimeSpan.FromMinutes(1))107 { 108 this.\_connectionString =
connectionString;109 } 110 111 protected override DatabaseConnection Create()
112 { 113 return new DatabaseConnection(\_connectionString); 114 } 115 116
public override void Expire(DatabaseConnection connection) 117 { 118
connection.Dispose(); 119 } 120 121 public override bool
Validate(DatabaseConnection connection) 122 { 123 return connection.IsOpen; 124
} 125 } 126 127 public class Client 128 { 129 public static void TestCase1() 130
{ 131 // Create the ConnectionPool:132 DatabaseConnectionPool pool = new
DatabaseConnectionPool( 133 "Data Source=DENNIS;Initial
Catalog=TESTDB;Integrated Security=True;");134 135 // Get a connection:136
DatabaseConnection connection = pool.CheckOut(); 137 138 // Use the
connection139 140 // Return the connection:141 pool.CheckIn(connection); 142 }
143 } 144 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
**实现方式(二):使用对象构造方法和预分配方式实现 ObjectPool 类。**
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace ObjectPoolPattern.Implementation2 2 { 3 /// \<summary\> 4 /// 对象池
5 /// \</summary\> 6 /// \<typeparam name="T"\>对象类型\</typeparam\> 7 public
class ObjectPool\<T\> where T : class 8 { 9 private readonly Func\<T\>
\_objectFactory; 10 private readonly ConcurrentQueue\<T\> \_queue = new
ConcurrentQueue\<T\>(); 11 12 /// \<summary\>13 /// 对象池 14 /// \</summary\>15
/// \<param name="objectFactory"\>构造缓存对象的函数\</param\>16 public
ObjectPool(Func\<T\> objectFactory) 17 { 18 \_objectFactory = objectFactory; 19
} 20 21 /// \<summary\>22 /// 构造指定数量的对象 23 /// \</summary\>24 ///
\<param name="count"\>数量\</param\>25 public void Allocate(int count) 26 { 27
for (int i = 0; i \< count; i++)28 \_queue.Enqueue(\_objectFactory()); 29 } 30
31 /// \<summary\>32 /// 缓存一个对象 33 /// \</summary\>34 /// \<param
name="obj"\>对象\</param\>35 public void Enqueue(T obj) 36 { 37
\_queue.Enqueue(obj); 38 } 39 40 /// \<summary\>41 /// 获取一个对象 42 ///
\</summary\>43 /// \<returns\>对象\</returns\>44 public T Dequeue() 45 { 46 T
obj; 47 return !\_queue.TryDequeue(out obj) ? \_objectFactory() : obj; 48 } 49 }
50 51 class Program 52 { 53 static void Main(string[] args)54 { 55 var pool =
new ObjectPool\<byte[]\>(() =\> new byte[65535]);56 pool.Allocate(1000);57 58
var buffer = pool.Dequeue(); 59 60 // .. do something here ..61 62
pool.Enqueue(buffer); 63 } 64 } 65 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码

View File

@@ -0,0 +1,129 @@
设计模式之美Observer观察者
**索引**
- 意图
- 结构
- 参与者
- 适用性
- 效果
- 相关模式
- 实现
- 实现方式Observer 模式结构样式代码。
**别名**
- Dependency
- Publish-Subscribe
**意图**
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
Define a one-to-many dependency between objects so that when one object changes
state, all its dependents are notified and updated automatically.
**结构**
![244171718081.png](media/7f6de13b5ce6b6c8c6ed6422472d1c32.png)
**参与者**
Subject
- Subject 知道它的 Observer。可以有任意多个 Observer 观察同一个 Subject。
- 提供注册和删除 Observer 的接口。
Observer
- 为那些在 Subject 发生改变时需要获得通知的对象定义一个 Update 接口。
ConcreteSubject
- 将有关状态存储各个 ConcreteObserver 对象。
- 当它的状态发生改变时,想它的各个 Observer 发出通知。
ConcreteObserver
- 维护一个指向 ConcreteSubject 对象的引用。
- 存储有关状态,这些状态应该与 ConcreteSubject 的状态保持一致。
- 实现 Observer 的更新接口以使自身状态与 ConcreteSubject 状态保持一致。
**适用性**
在以下情况下可以使用 Observer 模式:
- 当一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
- 当对一个对象的改变需要同时改变其他对象,而不知道具体有多少对象有待改变。
- 当一个对象必须通知其他对象,而它又不能假定其他对象时谁。
**效果**
- 目标与观察者间的抽象耦合。
- 支持广播通信。
- 意外的更新。因为 Observer 并不知道其他 Observer 的存在,所以对改变 Subject
的最终代价一无所知。
**相关模式**
- 可以使用 Mediator 模式封装复杂的更新语义,充当 Subject 与 Observer
之间的中介者。
**实现**
下面的交互图描述 Subject 与 Observer 之间的协作:
![257242027338.png](media/474507cff41bb4cb9eb9b309aa3f9d44.png)
**实现方式Observer 模式结构样式代码。**
**推模式Push Model**Subject 向 Observer
发送关于改变的详细信息,而不管它们是否需要。
**拉模式Pull Model**Subject 除最小通知外什么也不推送,由 Observer 显式地向
Subject 询问细节。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace ObserverPattern.Implementation1 2 { 3 public abstract class Observer
4 { 5 public abstract void Update(); 6 } 7 8 public abstract class Subject 9 {
10 private List\<Observer\> \_observers = new List\<Observer\>(); 11 12 public
void Attach(Observer observer) 13 { 14 \_observers.Add(observer); 15 } 16 17
public void Detach(Observer observer) 18 { 19 \_observers.Remove(observer); 20 }
21 22 public void Notify() 23 { 24 foreach (var observer in \_observers) 25 { 26
observer.Update(); 27 } 28 } 29 } 30 31 public class ConcreteSubject : Subject
32 { 33 private string \_state; 34 35 public string State 36 { 37 get38 { 39
return \_state; 40 } 41 set42 { 43 \_state = value; 44 Notify(); 45 } 46 } 47 }
48 49 public class ConcreteObserver : Observer 50 { 51 private ConcreteSubject
\_subject; 52 53 public ConcreteObserver(string name, ConcreteSubject subject)
54 { 55 Name = name; 56 \_subject = subject; 57 } 58 59 public string Name {
get; private set; }60 61 public override void Update() 62 { 63 string
subjectState = \_subject.State; 64 Console.WriteLine(Name + ": " +
subjectState); 65 } 66 } 67 68 public class Client 69 { 70 public void
TestCase1() 71 { 72 var subject = new ConcreteSubject(); 73 subject.Attach(new
ConcreteObserver("Observer 1", subject));74 subject.Attach(new
ConcreteObserver("Observer 2", subject));75 subject.Attach(new
ConcreteObserver("Observer 3", subject));76 77 subject.State = "Hello World";78
} 79 } 80 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码

View File

@@ -0,0 +1,72 @@
**索引**
- 意图
- 结构
- 参与者
- 适用性
- 效果
- 实现
- 实现方式(一):实现对初始化数据的封装。
**意图**
封装类的初始化数据,控制对类的属性的更改,并保持类数据与使用数据的方法间的隔离。
Encapsulate class data initialization, control write access to class attributes
and separate data from methods that use it.
**结构**
![331165412198.png](media/3002203a9f9b82e63b10eafc66f0fcb3.png)
**参与者**
MainClass
- 根据构造函数参数列表构造 DataClass 类的实例。
DataClass
- 封装数据。
**适用性**
当以下情况成立时可以使用 Private Class Data 模式:
- 类的初始化数据是一次性的不可修改的数据。
- 需要控制对类的初始化数据的更改。
- 预防对初始化数据的不必要的更改。
**效果**
- 减少类对外暴露的属性。
- 从类中移除了对数据的写权限。
**实现**
**实现方式(一):实现对初始化数据的封装。**
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace PrivateClassDataPattern.Implementation1 2 { 3 public class
CircleData 4 { 5 public CircleData(double radius, Color color, Point origin) 6 {
7 this.Radius = radius; 8 this.Color = color; 9 this.Origin = origin;10 } 11 12
public double Radius { get; private set; }13 public Color Color { get; private
set; }14 public Point Origin { get; private set; }15 } 16 17 public class Circle
18 { 19 private CircleData \_circleData; 20 21 public Circle(double radius,
Color color, Point origin) 22 { 23 \_circleData = new CircleData(radius, color,
origin); 24 } 25 26 public double Circumference 27 { 28 get { return 2 \*
Math.PI \* \_circleData.Radius; } 29 } 30 31 public double Diameter 32 { 33 get
{ return 2 \* \_circleData.Radius; } 34 } 35 36 public void Draw(Graphics
graphics) 37 { 38 } 39 } 40 }

View File

@@ -0,0 +1,152 @@
设计模式之美Product Trader操盘手
**索引**
- 意图
- 结构
- 参与者
- 适用性
- 效果
- 相关模式
- 实现
- 实现方式Product Trader 的示例实现。
**意图**
使客户程序可以通过命名抽象超类和给定规约来创建对象。
Product Trader 让客户程序与 Product
类解耦,从而使得类的层级结构、框架和应用程序易于改写、配置和演进。
Let clients create objects by naming an abstract superclass and by providing a
specification.
A Product Trader decouples the client from the product and thereby eases the
adaption, configuration and evolution of class hierarchies, frameworks and
applications.
**结构**
![312371996681.png](media/59d8d51a3c8b682d64bde56b4b6e641f.png)
**参与者**
Client
- 为 ConcreteProduct 类创建 Specification。
- 为 Product Trader 提供 Specification 以初始话构建过程。
Product
- 定义类层次的接口。
ConcreteProduct
- Product 抽象类的具体类。
- 提供足够的信息以判定是否满足 Specification。
ProductTrader
- 从 Client 接收一个 ConcreteProduct 对应的 Specification。
- 映射 Specification 和 Creator。
- 提供映射配置机制。
- 调用 Creator 以生成符合 Specification 的 ConcreteProduct。
Creator
- 定义创建 ConcreteProduct 实例的接口。
- 知道如何根据 Specification 创建合适的 ConcreteProduct。
Specification
- 一个 Specification 代表着一个 ConcreteProduct 类。
- 作为映射和查询 Creator 的条件参数。
![313005595380.png](media/372aa1b79b9805da8ffaeb2680a2e138.png)
**适用性**
当以下情况成立时可以使用 Product Trader 模式:
- 当你想让客户程序完全独立于 Product 实体类的实现时。
- 你需要在运行时根据可用的规约条件动态的生成 Product 对象时。
- 你需要为给定的规约条件配置相应的 Product 类对象。
- 你需要在不影响客户代码的条件下修改和演进 Product 类的层次。
**效果**
- Client 程序完全独立于 ConcreteProduct 类层次。
- 可以在运行时决定 Product 的具体类。
- 可以根据特定的领域对 Product 进行配置。
- Product 类层次更易于演进。
- 衍生新的 ConcreteProduct 更加方便。
- Product 类可以是负责的组件。
**相关模式**
- 可以尝试在 Factory Method 模式无法工作或不太适合时,尝试使用 Product
Trader。Factory Method 常使 Product 和 Creator 之间形成循环依赖。
**实现**
**实现方式Product Trader 的示例实现。**
1 namespace ProductTraderPattern.Implementation1 2 { 3 public class
Specification 4 { 5 public string Criteria { get; set; } 6 7 public bool
IsSatisfiedBy(Product product) 8 { 9 return product.Criteria == this.Criteria;
10 } 11 12 public override int GetHashCode() 13 { 14 return
Criteria.GetHashCode(); 15 } 16 17 public override bool Equals(object obj) 18 {
19 return GetHashCode().Equals(obj.GetHashCode()); 20 } 21 } 22 23 public
abstract class Product 24 { 25 public abstract string Criteria { get; } 26 } 27
28 public class ConcreteProductA : Product 29 { 30 public override string
Criteria 31 { 32 get 33 { 34 return "SpecForConreteProductA"; 35 } 36 } 37 } 38
39 public class ConcreteProductB : Product 40 { 41 public override string
Criteria 42 { 43 get 44 { 45 return "SpecForConreteProductB"; 46 } 47 } 48 } 49
50 public abstract class ProductCreator 51 { 52 public abstract Product
Create(Specification spec); 53 } 54 55 public class ConcreteProductCreator :
ProductCreator 56 { 57 public override Product Create(Specification spec) 58 {
59 if (spec.Criteria == "SpecForConreteProductA") 60 { 61 return new
ConcreteProductA(); 62 } 63 else if (spec.Criteria == "SpecForConreteProductB")
64 { 65 return new ConcreteProductB(); 66 } 67 68 // any factory you can use
here 69 throw new NotSupportedException(); 70 } 71 } 72 73 public class
ProductTrader 74 { 75 private Dictionary\<Specification, ProductCreator\> \_dict
76 = new Dictionary\<Specification, ProductCreator\>(); 77 78 public Product
CreateFor(Specification spec) 79 { 80 ProductCreator creator =
LookupCreator(spec); 81 Product product = creator.Create(spec); 82 return
product; 83 } 84 85 public ProductCreator LookupCreator(Specification spec) 86 {
87 return \_dict[spec]; 88 } 89 90 public void AddCreator(Specification spec,
ProductCreator creator) 91 { 92 \_dict.Add(spec, creator); 93 } 94 95 public
void RemoveCreator(Specification spec, ProductCreator creator) 96 { 97
\_dict.Remove(spec); 98 } 99 100 public void SubstituteCreator(Specification
spec, ProductCreator creator) 101 { 102 \_dict[spec] = creator; 103 } 104 } 105
106 public class Client 107 { 108 public void TestCase1() 109 { 110
Specification spec1 = new Specification(); 111 spec1.Criteria =
"SpecForConreteProductA";112 113 Specification spec2 = new Specification(); 114
spec2.Criteria = "SpecForConreteProductA";115 116 ProductCreator creator = new
ConcreteProductCreator(); 117 118 ProductTrader trader = new ProductTrader();
119 trader.AddCreator(spec1, creator); 120 trader.AddCreator(spec2, creator);
121 122 Specification spec3 = new Specification(); 123 spec3.Criteria =
"SpecForConreteProductA";124 125 Product product = trader.CreateFor(spec3); 126
} 127 } 128 }

View File

@@ -0,0 +1,275 @@
设计模式之美Prototype原型
**索引**
- 别名
- 意图
- 结构
- 参与者
- 适用性
- 缺点
- 效果
- 相关模式
- 命名约定
- 实现
- 实现方式(一):使用一个原型管理器。
- 实现方式使用浅拷贝实现克隆Clone操作。
- 实现方式使用深拷贝实现克隆Clone操作。
- 实现方式(四):初始化克隆对象。
**别名**
- Clone
**意图**
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
Specify the kinds of objects to create using a prototypical instance, and create
new objects by copying this prototype.
**结构**
![049306316682.png](media/034ce7ebd324d04b21ef2ba935649ff9.png)
**参与者**
Prototype
- 声明一个克隆自身的接口。
ConcretePrototype
- 实现一个克隆自身的操作。
Client
- 让一个原型克隆自身从而创建一个新的对象。
**适用性**
在以下情况下可以使用 Prototype 模式:
- 一个系统要独立于它的产品的创建、构成和表示时。
- 当要实例化的类是在运行时刻指定时,例如:通过动态装载。
- 为了避免创建一个与产品类层次平行的工厂类层次时。
- 当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。
**缺点**
- 每一个 Prototype 子类都必须实现 Clone
操作。当内部包括一些不支持拷贝或有循环引用的对象时,实现克隆可能也会很困难。
**效果**
- 它对客户隐藏了具体的产品类,因此减少了客户知道的名字的数目。
- 使客户无需改变即可使用与特定应用相关的类。
- 运行时刻增加和删除产品。
- 改变值以指定新对象。
- 改变结构以指定新对象。
- 减少子类的构造。
- 用类动态配置应用。
**相关模式**
- Abstract Factory 可以用
[Prototype ](http://www.cnblogs.com/gaochundong/p/design_pattern_prototype.html)来实现。
- Composite 和
[Decorator ](http://www.cnblogs.com/gaochundong/p/design_pattern_decorator.html)模式的设计也可以从
Prototype 获益。
**命名约定**
使用命名约定是一个好习惯,例如,总是声明那些实现克隆的操作为 Clone()。
**实现**
**实现方式(一):使用一个原型管理器。**
当一个系统中原型数目不固定时可以保持一个可用原型的注册表用以存储和检索原型。我们称这个注册表为原型管理器Prototype
Manager
客户在克隆一个原型前会先向注册表请求该原型。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace PrototypePattern.Implementation1 2 { 3 public abstract class
AbstractOrInterfaceOfPrototypeProduct 4 { 5 public int ValueProperty1 { get;
set; } 6 7 public abstract AbstractOrInterfaceOfPrototypeProduct Clone(); 8 } 9
10 public class ConcretePrototypeProductA :
AbstractOrInterfaceOfPrototypeProduct 11 { 12 public override
AbstractOrInterfaceOfPrototypeProduct Clone() 13 { 14 return new
ConcretePrototypeProductA() 15 { 16 ValueProperty1 = this.ValueProperty1,17 };
18 } 19 } 20 21 public class ConcretePrototypeProductB :
AbstractOrInterfaceOfPrototypeProduct 22 { 23 public override
AbstractOrInterfaceOfPrototypeProduct Clone() 24 { 25 return new
ConcretePrototypeProductB() 26 { 27 ValueProperty1 = this.ValueProperty1,28 };
29 } 30 } 31 32 public class ProductPrototypeManager 33 { 34 private
Dictionary\<string, AbstractOrInterfaceOfPrototypeProduct\> \_registry35 = new
Dictionary\<string, AbstractOrInterfaceOfPrototypeProduct\>();36 37 public void
Register(string name, 38 AbstractOrInterfaceOfPrototypeProduct prototypeProduct)
39 { 40 \_registry[name] = prototypeProduct; 41 } 42 43 public void
Unregister(string name) 44 { 45 \_registry.Remove(name); 46 } 47 48 public
AbstractOrInterfaceOfPrototypeProduct Retrieve(string name) 49 { 50 return
\_registry[name]; 51 } 52 53 public bool IsRegisterd(string name) 54 { 55 return
\_registry.ContainsKey(name); 56 } 57 } 58 59 public class Client 60 { 61 public
void TestCase1() 62 { 63 AbstractOrInterfaceOfPrototypeProduct prototypeProduct1
= new ConcretePrototypeProductA(); 64 AbstractOrInterfaceOfPrototypeProduct
prototypeProduct2 = new ConcretePrototypeProductB(); 65 66
ProductPrototypeManager manager = new ProductPrototypeManager(); 67
manager.Register("PrototypeProduct1", prototypeProduct1);68
manager.Register("PrototypeProduct2", prototypeProduct2);69 70
AbstractOrInterfaceOfPrototypeProduct clonedProduct1 =
manager.Retrieve("PrototypeProduct1").Clone();71 72 if
(manager.IsRegisterd("PrototypeProduct2"))73 { 74
AbstractOrInterfaceOfPrototypeProduct clonedProduct2 =
manager.Retrieve("PrototypeProduct2").Clone();75 } 76 } 77 } 78 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
**实现方式使用浅拷贝实现克隆Clone操作。**
Prototype 模式最困难的部分在于正确的实现 Clone 操作。
浅拷贝Shallow
Copy在拷贝时只复制对象所有字段的值。如果字段是值类型则复制其值如果字段是引用类型则复制引用指针。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace PrototypePattern.Implementation2 2 { 3 public class ReferencedClass
4 { 5 public int ReferencedClassProperty1 { get; set; } 6 } 7 8 public abstract
class AbstractOrInterfaceOfPrototypeProduct 9 { 10 public int ValueProperty1 {
get; set; }11 public ReferencedClass ReferenceProperty2 { get; set; }12 13
public abstract AbstractOrInterfaceOfPrototypeProduct Clone(); 14 } 15 16 public
class ConcreteShallowCopyPrototypeProductA 17 :
AbstractOrInterfaceOfPrototypeProduct 18 { 19 public
ConcreteShallowCopyPrototypeProductA() 20 { 21 this.ReferenceProperty2 = new
ReferencedClass() 22 { 23 ReferencedClassProperty1 = 11124 }; 25 } 26 27 public
override AbstractOrInterfaceOfPrototypeProduct Clone() 28 { 29 return new
ConcreteShallowCopyPrototypeProductA() 30 { 31 ValueProperty1 =
this.ValueProperty1,32 ReferenceProperty2 = this.ReferenceProperty2,33 }; 34 }
35 } 36 37 public class Client 38 { 39 public void TestCase2() 40 { 41
AbstractOrInterfaceOfPrototypeProduct prototypeProduct1 = new
ConcreteShallowCopyPrototypeProductA(); 42 AbstractOrInterfaceOfPrototypeProduct
clonedProduct1 = prototypeProduct1.Clone(); 43 bool areEqual1 =
object.ReferenceEquals(44 prototypeProduct1.ReferenceProperty2, 45
clonedProduct1.ReferenceProperty2); 46 } 47 } 48 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
**实现方式使用深拷贝实现克隆Clone操作。**
深拷贝Deep Copy涉及对源对象整个结构的拷贝。
深拷贝在拷贝时复制对象的所有字段的值。如果字段是值类型,则复制其值;如果字段是引用类型,则会将这个引用指针指向的对象也克隆一份。
可以通过序列化和反序列化来实现深拷贝。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace PrototypePattern.Implementation3 2 { 3 public class ReferencedClass
4 { 5 public int ReferencedClassProperty1 { get; set; } 6 } 7 8 public abstract
class AbstractOrInterfaceOfPrototypeProduct 9 { 10 public int ValueProperty1 {
get; set; }11 public ReferencedClass ReferenceProperty2 { get; set; }12 13
public abstract AbstractOrInterfaceOfPrototypeProduct Clone(); 14 } 15 16 public
class ConcreteShallowCopyPrototypeProductA 17 :
AbstractOrInterfaceOfPrototypeProduct 18 { 19 public
ConcreteShallowCopyPrototypeProductA() 20 { 21 this.ReferenceProperty2 = new
ReferencedClass() 22 { 23 ReferencedClassProperty1 = 111 24 }; 25 } 26 27 public
override AbstractOrInterfaceOfPrototypeProduct Clone() 28 { 29 return new
ConcreteShallowCopyPrototypeProductA() 30 { 31 ValueProperty1 =
this.ValueProperty1,32 ReferenceProperty2 = this.ReferenceProperty2,33 }; 34 }
35 } 36 37 public class ConcreteDeepCopyPrototypeProductB 38 :
AbstractOrInterfaceOfPrototypeProduct 39 { 40 public
ConcreteDeepCopyPrototypeProductB() 41 { 42 this.ReferenceProperty2 = new
ReferencedClass() 43 { 44 ReferencedClassProperty1 = 222 45 }; 46 } 47 48 public
override AbstractOrInterfaceOfPrototypeProduct Clone() 49 { 50 return new
ConcreteDeepCopyPrototypeProductB() 51 { 52 ValueProperty1 =
this.ValueProperty1,53 ReferenceProperty2 = new ReferencedClass() 54 { 55
ReferencedClassProperty1 = 56 this.ReferenceProperty2.ReferencedClassProperty157
}, 58 }; 59 } 60 } 61 62 public class Client 63 { 64 public void TestCase3() 65
{ 66 AbstractOrInterfaceOfPrototypeProduct prototypeProduct1 = new
ConcreteShallowCopyPrototypeProductA(); 67 AbstractOrInterfaceOfPrototypeProduct
clonedProduct1 = prototypeProduct1.Clone(); 68 bool areEqual1 =
object.ReferenceEquals(69 prototypeProduct1.ReferenceProperty2, 70
clonedProduct1.ReferenceProperty2); 71 72 AbstractOrInterfaceOfPrototypeProduct
prototypeProduct2 = new ConcreteDeepCopyPrototypeProductB(); 73
AbstractOrInterfaceOfPrototypeProduct clonedProduct2 =
prototypeProduct2.Clone(); 74 bool areEqual2 = object.ReferenceEquals(75
prototypeProduct2.ReferenceProperty2, 76 clonedProduct2.ReferenceProperty2); 77
78 Console.WriteLine("{0}, {1}", areEqual1, areEqual2);79 } 80 } 81 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
**实现方式(四):初始化克隆对象。**
客户可能会希望使用一些值来初始化该对象的内部状态。
但在 Clone 操作中传递参数会破坏克隆接口的统一性。
原型的类可以在 Clone 操作之后,调用包含初始化参数的 Initialize
方法来设定对象内部状态。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace PrototypePattern.Implementation4 2 { 3 public class ReferencedClass
4 { 5 public int ReferencedClassProperty1 { get; set; } 6 } 7 8 public abstract
class AbstractOrInterfaceOfPrototypeProduct 9 { 10 public int ValueProperty1 {
get; set; }11 public ReferencedClass ReferenceProperty2 { get; set; }12 13
public abstract AbstractOrInterfaceOfPrototypeProduct Clone(); 14 } 15 16 public
class ConcreteDeepCopyPrototypeProductB 17 :
AbstractOrInterfaceOfPrototypeProduct 18 { 19 public
ConcreteDeepCopyPrototypeProductB() 20 { 21 } 22 23 public void Initialize(int
propertyValue) 24 { 25 this.ValueProperty1 = propertyValue;26
this.ReferenceProperty2.ReferencedClassProperty1 = propertyValue;27 } 28 29
public override AbstractOrInterfaceOfPrototypeProduct Clone() 30 { 31 return new
ConcreteDeepCopyPrototypeProductB() 32 { 33 ValueProperty1 =
this.ValueProperty1,34 ReferenceProperty2 = new ReferencedClass() 35 { 36
ReferencedClassProperty1 = 37 this.ReferenceProperty2.ReferencedClassProperty138
}, 39 }; 40 } 41 } 42 43 public class Client 44 { 45 public void TestCase4() 46
{ 47 AbstractOrInterfaceOfPrototypeProduct prototypeProduct2 = new
ConcreteDeepCopyPrototypeProductB(); 48 ConcreteDeepCopyPrototypeProductB
clonedProduct2 = 49
(ConcreteDeepCopyPrototypeProductB)prototypeProduct2.Clone(); 50 51
clonedProduct2.Initialize(123);52 } 53 } 54 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码

View File

@@ -0,0 +1,126 @@
设计模式之美Proxy代理
**索引**
- 别名
- 意图
- 结构
- 参与者
- 适用性
- 效果
- 相关模式
- 实现
- 实现方式(一):使用相同 Subject 接口实现 Proxy。
**别名**
- Surrogate
**意图**
为其他对象提供一种代理以控制对这个对象的访问。
Provide a surrogate or placeholder for another object to control access to it.
**结构**
![505114124457.png](media/c13421b53c7c2b2146d2198f7ac4f85b.png)
运行时一种可能的 Proxy 结构的对象图:
![505464435187.png](media/37bc98b89e45d29981e717145b971aa0.png)
**参与者**
Proxy
- 保存一个引用使得代理可以访问实体。若 RealSubject 和 Subject
的接口相同Proxy 会引用 Subject。
- 提供一个与 Subject 的接口相同的接口,这样 Proxy 就可以用来代替实体。
- 控制对实体的存取,并可能负责创建和删除它。
- 其他功能依赖于 Proxy 的类型:
- 远程代理Remote
Proxy负责对请求及其参数进行编码并向不同地址空间中的实体发送已编码的请求。
- 虚拟代理Virtual Proxy可以缓存实体的附加信息以便延迟对它的访问。
- 保护代理Protection
Proxy检查调用者是否具有实现一个请求所必须的访问权限。
Subject
- 定义 RealSubject 和 Proxy 的共用接口,这样就在任何使用 RealSubject
的地方都可以使用 Proxy。
RealSubject
- 定义 Proxy 所代表的实体。
**适用性**
下面是一些使用 Proxy 模式的常见情况:
- 远程代理Remote Proxy为一个对象在不同的地址空间提供局部代表。
- 虚拟代理Virtual Proxy根据需要创建开销很大的对象。
- 保护代理Protection Proxy控制对原始对象的访问。
- 智能代理Smart Proxy在访问对象时执行一些附件操作。
**效果**
- Proxy 模式在访问对象时引入了一定程度的间接性。
- Proxy 模式可以对用户隐藏 Copy-On-Write 优化方式。用 Proxy
延迟对象拷贝过程,仅当这个对象被修改时才进行真正的拷贝,用以大幅度降低拷贝大实体的开销。
**相关模式**
- Adapter 为它所适配的对象提供了一个不同的接口。Proxy
提供了与它的实体相同的接口
- Decorator 的实现与 Proxy 相似,但目的不一样。 Decorator
为对象添加功能Proxy 则控制对对象的访问。
**实现**
**实现方式(一):使用相同 Subject 接口实现 Proxy。**
对一个对象进行访问控制的一个原因是为了只有在我们确实需要这个对象时才对它进行创建和初始化。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace ProxyPattern.Implementation1 2 { 3 public abstract class Subject 4 {
5 public abstract string Name { get; } 6 public abstract void Request(); 7 } 8 9
public class ConcreteSubject : Subject 10 { 11 private string \_name; 12 13
public ConcreteSubject(string name) 14 { 15 \_name = name; 16 } 17 18 public
override string Name { get { return \_name; } } 19 20 public override void
Request() 21 { 22 // do something23 } 24 } 25 26 public class Proxy : Subject 27
{ 28 private Subject \_realSubject = null;29 private string \_name; 30 31 public
Proxy(string name) 32 { 33 \_name = name; 34 } 35 36 public override string Name
{ get { return \_name; } } 37 38 public override void Request() 39 { 40 if
(\_realSubject == null)41 LoadRealSubject(); 42 43 \_realSubject.Request(); 44 }
45 46 private void LoadRealSubject() 47 { 48 // do some heavy things49
\_realSubject = new ConcreteSubject(_name); 50 } 51 } 52 53 public class Client
54 { 55 public void TestCase1() 56 { 57 Subject subject = new
Proxy("SubjectName");58 var subjectName = subject.Name; 59 subject.Request(); 60
} 61 } 62 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码

View File

@@ -0,0 +1,150 @@
**索引**
- 意图
- 结构
- 参与者
- 适用性
- 效果
- 相关模式
- 实现
- 实现方式Role Object 的示例实现。
**意图**
通过明确地附加角色对象到目标对象中,以使对象可以适配不同的客户需求。每个角色对象都代表着目标对象在客户上下文中的一种角色。每种上下文都存在于特定的应用程序中,因此可使对象在不同的应用程序间解耦。
Adapt an object to different clients needs through transparently attached role
objects, each one representing a role the object has to play in that clients
context. Each context may be its own application, which therefore gets decoupled
from the other applications.
**结构**
![242283386561.png](media/026ab145cc830444e57f84c06e105192.png)
**参与者**
Component
- 定义关键的抽象接口。
- 定义添加、删除、测试和查询角色对象的接口协议。Client 可以指定一个
specification 来获取一个 ConcreteRole 实例。
ComponentCore
- 实现 Component 接口,包括角色管理协议。
- 负责创建 ConcreteRole 实例。
- 管理角色对象。
ComponentRole
- 存储一个经过装饰的 ComponentCore 对象的引用。
- 实现 Component 接口,并将接口请求转发到 ComponentCore 中。
ConcreteRole
- 定义和实现特定上下文的 Component 接口的实现。
- 构造函数包含一个 ComponentCore 参数。
Client
- 请求协作对象。
**适用性**
当以下情况成立时可以使用 Role Object 模式:
- 你需要在不同的客户上下文间保持关键的抽象定义,每种抽象定义的实现都存在于其特定的应用范围内,同时你不想将这些与客户上下文相关的接口耦合在同一个接口内。
- 你需要能够动态的处理角色,以便可以在运行时按需的添加或移除角色,而不是在编译时使用固定的对象定义。
- 你需要保持角色与客户成对儿关系,在不同的角色与客户对之间保持独立性,当更改一个角色时并不影响其他客户。
**效果**
- 可以简洁地定义对象的关键抽象。Component
接口仅需关注必要的状态的行为,而不会涉及具体上下文中的职责。
- 角色对象易于演进,彼此之间保持独立。
- 角色对象可以动态的添加和移除。
- 应用程序可以更好的解耦和。
**相关模式**
- Decorator 模式与 Role Object 模式有着类似的结构但行为不同。Decorator
模式使开发人员可以对一个对象进行链式的装饰,而 Role Object
模式则不允许这样。并且Decorator 通常不会扩大对象的功能,而 Role Object
会引入新的操作。
- Extension Object
模式也在解决类似的问题通过对对象的扩展来满足特定的客户上下文的需求。尽管如此Extension
Object 并没有考虑透明性Transparently即保持对象关键抽象Key
Abstraction的定义而 Role Object 则正是强调了这一点。
**实现**
**实现方式Role Object 的示例实现。**
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace RoleObjectPattern.Implementation1 2 { 3 public abstract class
Customer 4 { 5 public abstract CustomerRole GetRole(string spec); 6 public
abstract void AddRole(string spec); 7 public abstract void RemoveRole(string
spec); 8 public abstract bool HasRole(string spec); 9 public abstract void
SomeCommonOperation1(); 10 } 11 12 public class CustomerCore : Customer 13 { 14
private Dictionary\<string, CustomerRole\> \_roles 15 = new Dictionary\<string,
CustomerRole\>(); 16 private CustomerRoleFactory \_roleFactory; 17 18 public
CustomerCore(CustomerRoleFactory roleFactory) 19 { 20 \_roleFactory =
roleFactory; 21 } 22 23 public override CustomerRole GetRole(string spec) 24 {
25 if (\_roles.ContainsKey(spec)) 26 return \_roles[spec]; 27 else 28 return
null; 29 } 30 31 public override void AddRole(string spec) 32 { 33 CustomerRole
role = GetRole(spec); 34 if (role == null) 35 { 36 role =
\_roleFactory.CreateRole(spec, this); 37 if (role != null) 38 { 39
\_roles.Add(spec, role); 40 } 41 } 42 } 43 44 public override void
RemoveRole(string spec) 45 { 46 \_roles.Remove(spec); 47 } 48 49 public override
bool HasRole(string spec) 50 { 51 return \_roles.ContainsKey(spec); 52 } 53 54
public override void SomeCommonOperation1() 55 { 56 // do some business logic 57
} 58 } 59 60 public class CustomerRole : Customer 61 { 62 private CustomerCore
\_decoratedCore; 63 64 public CustomerRole(CustomerCore core) 65 { 66
\_decoratedCore = core; 67 } 68 69 public override CustomerRole GetRole(string
spec) 70 { 71 return \_decoratedCore.GetRole(spec); 72 } 73 74 public override
void AddRole(string spec) 75 { 76 \_decoratedCore.AddRole(spec); 77 } 78 79
public override void RemoveRole(string spec) 80 { 81
\_decoratedCore.RemoveRole(spec); 82 } 83 84 public override bool HasRole(string
spec) 85 { 86 return \_decoratedCore.HasRole(spec); 87 } 88 89 public override
void SomeCommonOperation1() 90 { 91 \_decoratedCore.SomeCommonOperation1(); 92 }
93 } 94 95 public class Borrower : CustomerRole 96 { 97 public
Borrower(CustomerCore core) 98 : base(core) 99 { 100 } 101 102 public void
SomeOperationForBorrower() 103 { 104 // do something for borrower105 } 106 } 107
108 public class Investor : CustomerRole 109 { 110 public Investor(CustomerCore
core) 111 : base(core)112 { 113 } 114 115 public void SomeOperationForInvestor()
116 { 117 // do something for investor118 } 119 } 120 121 public class
CustomerRoleFactory 122 { 123 public CustomerRole CreateRole(string spec,
CustomerCore core) 124 { 125 CustomerRole newRole = null;126 127 if (spec ==
"Borrower")128 { 129 newRole = new Borrower(core); 130 } 131 else if (spec ==
"Investor")132 { 133 newRole = new Investor(core); 134 } 135 136 return newRole;
137 } 138 } 139 140 public class Client 141 { 142 public void TestCase1() 143 {
144 Customer customer = new CustomerCore(new CustomerRoleFactory()); 145
customer.AddRole("Borrower");146 customer.AddRole("Investor");147 148
CustomerRole customerRole1 = customer.GetRole("Borrower");149 Borrower borrower
= (Borrower)customerRole1; 150 borrower.SomeCommonOperation1(); 151
borrower.SomeOperationForBorrower(); 152 153 CustomerRole customerRole2 =
customer.GetRole("Investor");154 Investor investor = (Investor)customerRole2;
155 investor.SomeCommonOperation1(); 156 investor.SomeOperationForInvestor();
157 } 158 } 159 }

View File

@@ -0,0 +1,189 @@
设计模式之美Singleton单件
**索引**
- 意图
- 结构
- 参与者
- 适用性
- 缺点
- 效果
- 相关模式
- 实现
- 实现方式(一):使用 Static 变量初始化 Singleton。
- 实现方式(二):使用 Lazy Initialization 来实现 Singleton。
- 实现方式(三):使用 Reset 来重置 Singleton。
- 实现方式(四):使用 Double-Check Locking 技术实现 Singleton。
- 实现方式(五):使用注册表机制创建和查询 Singleton 类的子类实例。
**意图**
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
Ensure a class only has one instance, and provide a global point of access to
it.
**结构**
![216546462556.png](media/a8d3ed7b8538ad1d4b557cce8f56bc83.png)
**参与者**
Singleton
- 定义一个 Instance 操作允许客户访问它的唯一实例。Instance 是一个类操作。
- 可能负责创建它自己的唯一实例。
**适用性**
在以下情况下可以使用 Singleton 模式:
- 当类只能有一个实例并且客户可以从一个众所周知的访问点访问它时。
- 当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。
**缺点**
- 系统检查开销。实现中可能每次都需要检查实例是否存在,这个问题可以通过 Static
实例来解决。
- 系统资源开销。通常 Singleton
中的对象一旦被创建,不会被及时销毁。可以通过提供 Reset 操作来重置。
- 引起开发混淆。如果类包括 public 构造函数可以在外部构造,当使用 Singleton
对象时,开发人员需要记住不能使用 new 关键字来实例化对象。
- 不易于测试。通常使用 Singleton
时需要考虑是否是反模式,设计是否存在问题。引入 Singleton 或静态实例会为 Unit
Testing 带来困难。
**效果**
- 对唯一实例的受控访问。
- 缩小名空间。避免存储唯一实例的全局变量污染名空间
- 允许对操作和表示的精化。Singleton
类可以有子类,通过扩展类在运行时刻配置应用。
- 允许可变数目的实例。控制应用所使用的实例的数目。
- 比类操作更灵活。比如使用静态成员函数。
**相关模式**
- 很多模式可以使用 Singleton 模式实现。例如Abstract Factory 可以设计为
Singleton 实例。
**实现**
**实现方式(一):使用 Static 变量初始化 Singleton。**
在类加载时即创建实例。缺点是无论使用与否实例均被创建。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace SingletonPattern.Implementation1 2 { 3 public class Singleton 4 { 5
private static Singleton \_instance = new Singleton(); 6 7 // the constructor
should be protected or private 8 protected Singleton() 9 { 10 } 11 12 public
static Singleton Instance() 13 { 14 return \_instance; 15 } 16 } 17 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
**实现方式(二):使用 Lazy Initialization 来实现 Singleton。**
通常将创建类的唯一实例的操作隐藏在一个类操作后面,由它保证只有一个实例被创建。这个操作可以访问保存唯一实例的变量,保证在它的首次使用前被创建和初始化。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace SingletonPattern.Implementation2 2 { 3 public class Singleton 4 { 5
private static Singleton \_instance; 6 7 // the constructor should be protected
or private 8 protected Singleton() 9 { 10 } 11 12 public static Singleton
Instance() 13 { 14 if (_instance == null)15 { 16 // use lazy initialization17
\_instance = new Singleton(); 18 } 19 20 return \_instance; 21 } 22 } 23 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
**实现方式(三):使用 Reset 来重置 Singleton。**
可以使用 Reset 操作来将已创建的实例销毁掉。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace SingletonPattern.Implementation3 2 { 3 public class Singleton 4 { 5
private static Singleton \_instance; 6 7 // the constructor should be protected
or private 8 protected Singleton() 9 { 10 } 11 12 public static Singleton
Instance() 13 { 14 if (_instance == null)15 { 16 // use lazy initialization17
\_instance = new Singleton(); 18 } 19 20 return \_instance; 21 } 22 23 public
void Reset() 24 { 25 \_instance = null;26 } 27 } 28 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
**实现方式(四):使用 Double-Check Locking 技术实现 Singleton。**
Singleton 的实现如果需要保证线程安全性,则可以使用 Double-Check Locking 技术。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace SingletonPattern.Implementation4 2 { 3 public class Singleton 4 { 5
private static Singleton \_instance; 6 private static readonly object \_syncRoot
= new object(); 7 8 // the constructor should be protected or private 9
protected Singleton() 10 { 11 } 12 13 public static Singleton Instance() 14 { 15
// double-check locking16 if (_instance == null)17 { 18 lock (\_syncRoot) 19 {
20 if (_instance == null)21 { 22 // use lazy initialization23 \_instance = new
Singleton(); 24 } 25 } 26 } 27 28 return \_instance; 29 } 30 } 31 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
**实现方式(五):使用注册表机制创建和查询 Singleton 类的子类实例。**
如果系统中定义了多个 Singleton
的子类,可以实现一个注册表机制,用于存储子类的映射。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace SingletonPattern.Implementation5 2 { 3 public class Singleton 4 { 5
private static Dictionary\<string, Singleton\> \_registry 6 = new
Dictionary\<string, Singleton\>(); 7 private static Singleton \_instance; 8 9 //
the constructor should be protected or private10 protected Singleton() 11 { 12 }
13 14 public static Singleton Instance(string name) 15 { 16 if
(!\_registry.ContainsKey(name)) 17 { 18 if (name == "Apple")19 { 20
\_registry.Add(name, new AppleSingleton()); 21 } 22 else if (name == "Orange")23
{ 24 \_registry.Add(name, new OrangeSingleton()); 25 } 26 } 27 28 return
\_registry[name]; 29 } 30 } 31 32 public class AppleSingleton : Singleton 33 {
34 } 35 36 public class OrangeSingleton : Singleton 37 { 38 } 39 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码

View File

@@ -0,0 +1,109 @@
设计模式之美State状态
**索引**
- 意图
- 结构
- 参与者
- 适用性
- 效果
- 相关模式
- 实现
- 实现方式(一):由 ConcreteState 指定它的后继 State。
**意图**
允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。
Allow an object to alter its behavior when its internal state changes. The
object will appear to change its class.
**结构**
![547252187237.png](media/de271ae3dd89c9f862ae1b16714ff5db.png)
**参与者**
Context
- 定义客户感兴趣的接口。
- 维护一个 ConcreteState 子类的实例,这个实例定义当前状态。
State
- 定义一个接口以封装与 Context 的一个特定状态相关的额行为。
ConcreteState
- 每一个子类实现一个与 Context 的一个状态相关的行为。
**适用性**
在以下情况下可以使用 State 模式:
- 一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为。
- 一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。这个状态通常用一个或多个枚举常量表示。
**效果**
- 它将与特定状态相关的行为局部化。
- 它使得状态转换显式化。
- State 对象可被共享。
**相关模式**
- 使用 Flyweight 模式共享状态对象。
- 使用 Singleton 模式实现状态对象。
**实现**
**实现方式(一):由 ConcreteState 指定它的后继 State。**
State 模式不指定哪一个参与者定义状态转换规则。
如果该转换规则是固定的,那么它们可在 Context 中完全实现。
然而若让 State 子类自身指定它们的后继状态以及何时进行转换,通常更为灵活。
这需要 Context 增加一个接口,可以显示地设定 State。
这种实现的缺点是,一个 State
子类至少拥有一个其他子类的信息,使子类间产生了依赖。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace StatePattern.Implementation1 2 { 3 public abstract class State 4 { 5
public abstract string Name { get; } 6 public abstract void Handle(Context
context); 7 } 8 9 public class Context 10 { 11 private State \_state; 12 13
public Context() 14 { 15 } 16 17 public void SetState(State state) 18 { 19
\_state = state; 20 Console.WriteLine("Current State: {0}", \_state.Name);21 }
22 23 public void Request() 24 { 25 \_state.Handle(this);26 } 27 } 28 29 public
class ConcreteStateA : State 30 { 31 public override string Name { get { return
"StateA"; } }32 33 public override void Handle(Context context) 34 { 35
Console.WriteLine(Name + " is handling context.");36 37 // change context
state38 context.SetState(new ConcreteStateB()); 39 } 40 } 41 42 public class
ConcreteStateB : State 43 { 44 public override string Name { get { return
"StateB"; } }45 46 public override void Handle(Context context) 47 { 48
Console.WriteLine(Name + " is handling context.");49 50 // change context
state51 context.SetState(new ConcreteStateA()); 52 } 53 } 54 55 public class
Client 56 { 57 public void TestCase1() 58 { 59 var context = new Context(); 60
context.SetState(new ConcreteStateA()); 61 62 context.Request(); 63
context.Request(); 64 } 65 } 66 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码

View File

@@ -0,0 +1,137 @@
设计模式之美Strategy策略
**索引**
- 意图
- 结构
- 参与者
- 适用性
- 效果
- 相关模式
- 实现
- 实现方式(一):使用不同的 Strategy 处理内部状态。
**别名**
- Policy
**意图**
定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。使得算法可独立于使用它的客户而变化。
Define a family of algorithms, encapsulate each one, and make them
interchangeable.
Strategy lets the algorithm vary independently from clients that use it.
**结构**
![559166861981.png](media/28cabe60fc8136de5c80e251000dd2ee.png)
**参与者**
Strategy
- 定义所有支持的算法的公共接口。Context 使用这个接口来调用 ConcreteStrategy
定义的算法。
ConcreteStrategy
- 实现 Strategy 接口和具体算法。
Context
- 用一个 ConcreteStrategy 对象来配置。
- 维护一个对 Strategy 对象的引用。
- 可定义一个接口来让 Strategy 访问它的数据。
**适用性**
在以下情况下可以使用 Strategy 模式:
- 许多相关的类仅仅是行为有异。Strategy
提供了一种用多个行为中的一个行为来配置一个类的方法。
- 需要使用一个算法的不同变体。
- 算法使用客户不应该知道的数据。
- 一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关条件分支移入它们各自的
Strategy 类中以代替。
**缺点**
- 客户必须了解不同的 Strategy。要选择合适的 Strategy 就必须知道这些 Strategy
有何不同。
- Strategy 和 Context 之间的通信开销。Context 可能创建一些 ConcreteStrategy
不使用的参数。
- 增加了对象的数目。
**效果**
- 相关算法系列。
- 一个替代继承的方法。
- 消除了一些条件语句。
- 实现的选择。相同行为的不同实现。
**相关模式**
- 使用 Flyweight 模式实现 Strategy。
**实现**
**实现方式(一):使用不同的 Strategy 处理内部状态。**
Strategy 和 Context 接口必须使得 ConcreteStrategy 能够有效的访问它所需要的
Context 中的任何数据。
一种办法是让 Context 将数据放在参数中传递给 Strategy 操作。这使得 Strategy 和
Context 解耦。
但另一方面Context 可能发送一些 Strategy 不需要的数据。
另一种办法是让 Context 将自身作为一个参数传递给 Strategy该 Strategy
再显式地向该 Context 请求数据。
或者 Strategy 可以直接保存对 Context 的引用。
这种情况下Strategy 可以请求到它需要的数据。但这使得 Strategy 和 Context
更紧密的耦合在一起。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace StrategyPattern.Implementation1 2 { 3 public abstract class Strategy
4 { 5 public abstract void AlgorithmInterface(string state); 6 } 7 8 public
class ConcreteStrategyA : Strategy 9 { 10 public override void
AlgorithmInterface(string state) 11 { 12 Console.WriteLine("Use Concrete
Strategy A to handle " + state); 13 } 14 } 15 16 public class ConcreteStrategyB
: Strategy 17 { 18 public override void AlgorithmInterface(string state) 19 { 20
Console.WriteLine("Use Concrete Strategy B to handle " + state); 21 } 22 } 23 24
public class Context 25 { 26 private Strategy \_strategy; 27 28 public void
SetStrategy(Strategy strategy) 29 { 30 \_strategy = strategy; 31 } 32 33 public
string State { get; set; }34 35 public void ContextInterface() 36 { 37
\_strategy.AlgorithmInterface(State); 38 } 39 } 40 41 public class Client 42 {
43 public void TestCase1() 44 { 45 var context = new Context(); 46 context.State
= "Hello World";47 48 context.SetStrategy(new ConcreteStrategyA()); 49
context.ContextInterface(); 50 51 context.SetStrategy(new ConcreteStrategyB());
52 context.ContextInterface(); 53 } 54 } 55 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码

View File

@@ -0,0 +1,128 @@
设计模式之美Structural Patterns结构型模式
结构型模式涉及到如何组合类和对象以获得更大的结构。
- 结构型类模式采用继承机制来组合接口实现。
- 结构型对象模式不是对接口和实现进行组合,而是描述了如何对一些对象进行组合,从而实现新功能的一些方法。
因为可以在运行时改变对象组合关系,所以对象组合方式具有更大的灵活性,而这种机制用静态组合是不可能实现的。
- **Adapter适配器**
- 将一个类的接口转换成客户希望的另外一个接口。
- Adapter 模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
- Convert the interface of a class into another interface clients expect.
- Adapter lets classes work together that couldn't otherwise because of
incompatible interfaces.
- **Bridge桥接**
- 将抽象部分与它的实现部分分离,使它们都可以独立地变化。
- Decouple an abstraction from its implementation so that the two can vary
independently.
- **Composite组合**
- 将对象组合成树形结构以表示 “部分-整体” 的层次结构。
- Composite 使得用户对于单个对象和组合对象的使用具有一致性。
- Compose objects into tree structures to represent part-whole
hierarchies.
- Composite lets clients treat individual objects and compositions of
objects uniformly.
- **Decorator装饰**
- 动态地给一个对象添加一些额外的职责。
- 就增加功能来说Decorator 模式相比生成子类更为灵活。
- Attach additional responsibilities to an object dynamically.
- Decorators provide a flexible alternative to subclassing for extending
functionality.
- **Facade外观**
- 为子系统中的一组接口提供一个一致的界面。
- Facade 模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
- Provide a unified interface to a set of interfaces in a subsystem.
- Facade defines a higher-level interface that makes the subsystem easier
to use.
- **Flyweight享元**
- 运用共享技术有效地支持大量细粒度的对象。
- Use sharing to support large numbers of fine-grained objects
efficiently.
- **Proxy代理**
- 为其他对象提供一种代理以控制对这个对象的访问。
- Provide a surrogate or placeholder for another object to control access
to it.
结构型模式之间存在很多相似性,尤其是它们的参与者和协作之间的相似性。
这是因为结构型模式依赖于同一个很小的语言机制集合构造代码和对象:
- 单继承和多重继承机制用于基于类的模式。
- 对象组合机制用于对象式模式。
**Adapter 和 Bridge 的相似性**
Adapter 模式和 Bridge
模式具有一些共同的特征。它们之间的不同之处主要在于它们各自的用途。
Adapter
模式主要是为了解决两个已有接口之间不匹配的问题。它不考虑这些接口时怎么实现的,也不考虑它们各自可能会如何演化。
Bridge 模式则对抽象接口和它的实现部分进行桥接。它为用户提供了一个稳定的接口。
Adapter 模式和 Bridge 模式通常被用于软件生命周期的不同阶段。
当你发现两个不兼容的类必须同时工作时,就有必要使用 Adapter
模式,以避免代码重复。此处耦合不可见。
相反Bridge
的使用者必须事先知道:一个抽象将有多个实现部分,并且抽象和实现两者是独立演化得。
**Composite 和 Decorator 的相似性**
Composite 和 Decorator
模式具有类似的结构图,这说明它们都基于递归组合来组织可变数目的对象。
Decorator
旨在使你能够不需要生成子类即可给对象添加职责。这就避免了静态实现所有功能组合,从而导致子类急剧增加。
Composite
则有不同的目的,它旨在构造类,使多个相关的对象能够以统一的方式处理,而多重对象可以被当作一个对象来处理。它重点不在于修饰,而在于表示。
**Decorator 和 Proxy 的相似性**
Decorator 和 Proxy 模式描述了怎样为对象提供一定程度上的间接引用。
Decorator 和 Proxy
对象的实现部分都保留了指向另一个对象的指针,它们向这个对象发送请求。
同样,它们也具有不同的设计目的。
Proxy
不能动态地添加和分离性质,它也不是为递归组合而设计的。它的目的是,当直接访问一个实体不方便或不符合需要时,为这个实体提供一个替代者。
在 Proxy 中,实体定义了关键功能,而 Proxy 提供对它的访问。
在 Decorator 中,组件仅提供了部分功能,而 Decorator 负责完成其他功能。

View File

@@ -0,0 +1,120 @@
设计模式之美Template Method模板方法
**索引**
- 意图
- 结构
- 参与者
- 适用性
- 效果
- 相关模式
- 实现
- 实现方式Template Method 模式结构样式代码。
**意图**
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。
Template Method 使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
Define the skeleton of an algorithm in an operation, deferring some steps to
subclasses.
Template Method lets subclasses redefine certain steps of an algorithm without
changing the algorithm's structure.
**结构**
![618336243562.png](media/62502293d8ec3c5ee0cb6130ba088ad9.png)
**参与者**
AbstractClass
- 定义抽象的原语操作Primitive
Operation具体的子类将重定义它们以实现一个算法的各步骤。
- 实现一个模板方法,定义一个算法的骨架。该模板方法不仅调用原语操作,也调用定义在类中的其他操作。
ConcreteClass
- 实现原语操作以完成算法中与特定子类相关的步骤。
**适用性**
在以下情况下可以使用 Template Method 模式:
- 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
- 各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。
- 控制子类扩展。只允许特定的点进行扩展。
**效果**
- 模板方法是一种代码复用的基本技术。
- “好莱坞法则Don't call us, we'll call
you.”,一个父类调用一个子类的操作,而不是相反。
**相关模式**
- Factory Method 常被 Template Method 调用。
- Template Method 使用继承来改变算法的一部分。Strategy
使用委托来改变整个算法。
**实现**
Template Method 可调用下列类型的操作:
- 对客户的操作
- 具体的 AbstractClass 中的操作
- Factory Method 方法
- 抽象操作Primitive Operation(must be overridden)
- 钩子操作Hook Operation(may be overridden),通常提供默认实现。
Template Method 需要指明哪些是 Hook Operation哪些是 Primitive
Operation。例如使用命名约定等方式指明。
**实现方式Template Method 模式结构样式代码。**
定义 Template Method
的一个重要的目的是尽量减少一个子类具体实现该算法时必须重定义的那些原语操作的数目。
需要重定义的操作越多,客户程序就越冗长。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 namespace TemplateMethodPattern.Implementation1 2 { 3 public abstract class
Algorithm 4 { 5 public void TemplateMethod() 6 { 7 Step1CanNotBeCustomized(); 8
Step2(); 9 Step3WithDefault(); 10 } 11 12 private void Step1CanNotBeCustomized()
13 { 14 Console.WriteLine("Step1");15 } 16 17 protected abstract void Step2();
18 19 protected virtual void Step3WithDefault() 20 { 21
Console.WriteLine("Default Step3");22 } 23 } 24 25 public class
ConcreteAlgorithmA : Algorithm 26 { 27 protected override void Step2() 28 { 29
Console.WriteLine("ConcreteAlgorithmA.Step2");30 } 31 } 32 33 public class
ConcreteAlgorithmB : Algorithm 34 { 35 protected override void Step2() 36 { 37
Console.WriteLine("ConcreteAlgorithmB.Step2");38 } 39 40 protected override void
Step3WithDefault() 41 { 42 Console.WriteLine("ConcreteAlgorithmB.Step3");43 } 44
} 45 46 public class Program 47 { 48 public void TestCase1() 49 { 50 var
algorithm1 = new ConcreteAlgorithmA(); 51 algorithm1.TemplateMethod(); 52 53 var
algorithm2 = new ConcreteAlgorithmB(); 54 algorithm2.TemplateMethod(); 55 } 56 }
57 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码

Some files were not shown because too many files have changed in this diff Show More