diff --git a/设计模式/EntityFramework中使用Repository装饰器.md b/设计模式/EntityFramework中使用Repository装饰器.md new file mode 100644 index 00000000..5d5b6349 --- /dev/null +++ b/设计模式/EntityFramework中使用Repository装饰器.md @@ -0,0 +1,176 @@ +EntityFramework中使用Repository装饰器 + +**铺垫** + +通常在使用 EntityFramework 时,我们会封装出 IRepository 和 IUnitOfWork +接口,前者负责 CRUD 操作,后者负责数据提交 Commit。 + +![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif) + +复制代码 + +1 public interface IRepository\ 2 where T : class 3 { 4 IQueryable\ +Query(); 5 6 void Insert(T entity); 7 8 void Update(T entity, params +Expression\\>[] 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\ factory = () =\> 2 { 3 return new UnityContainer() 4 +.RegisterType(typeof(IRepository\<\>), typeof(Repository\<\>), new +ContainerControlledLifetimeManager()) 5 .RegisterType\(new ContainerControlledLifetimeManager()) 6 +.RegisterType\(new +ContainerControlledLifetimeManager()) 7 .RegisterType\(new +ContainerControlledLifetimeManager()) 8 .RegisterType\(new ContainerControlledLifetimeManager()) 9 +.RegisterType\(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\ 实现,在每个方法中嵌入计数器。 + +4. 继承 Repository\ 类,在衍生类中嵌入计数器。 + +5. 使用装饰器模式封装 Repository\,在新的 RepositoryDecorator\ + 类中嵌入计数器。 + +考虑到前三种方法均需要改动已有代码,主要是涉及的修改太多,所有没有尝试采用。 + +方法 4 则要求修改 Repository\ 的实现,为 CRUD 方法添加 virtual +关键字以便扩展。 + +方法 5 不需要修改 Repository\ 的实现,对已有代码的改动不大。 + +综上所述,我们选择了方法 5。 + +**Repository 装饰器基类实现** + +为便于以后的扩展,创建一个装饰器的抽象类。 + +![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif) + +复制代码 + +1 public abstract class RepositoryDecorator\ : IRepository\ 2 where T : +class 3 { 4 private readonly IRepository\ \_surrogate; 5 6 protected +RepositoryDecorator(IRepository\ surrogate) 7 { 8 \_surrogate = surrogate; 9 +} 10 11 protected IRepository\ Surrogate 12 { 13 get { return \_surrogate; } +14 } 15 16 \#region IRepository\ Members 17 18 public virtual +IQueryable\ 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\\>[] +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\ 类型仍然实现了 IRepository\ +接口,对外使用没有任何变化。 + +**实现需求** + +我们定义一个 CountableRepository\ 类用于封装 CRUD 计数功能,其继承自 +RepositoryDecorator\ 抽象类。 + +![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif) + +复制代码 + +1 public class CountableRepository\ : RepositoryDecorator\ 2 where T : +class 3 { 4 public CountableRepository(IRepository\ surrogate) 5 : +base(surrogate) 6 { 7 } 8 9 public override IQueryable\ Query() 10 { 11 +PerformanceCounter.CountQuery\(); 12 return base.Query();13 } 14 15 public +override void Insert(T entity) 16 { 17 PerformanceCounter.CountInsert\(); 18 +base.Insert(entity);19 } 20 21 public override void Update(T entity, params +Expression\\>[] modifiedPropertyLambdas)22 { 23 +PerformanceCounter.CountUpdate\(); 24 base.Update(entity, +modifiedPropertyLambdas);25 } 26 27 public override void Delete(T entity) 28 { +29 PerformanceCounter.CountDelete\(); 30 base.Delete(entity);31 } 32 } + +![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif) + +复制代码 + +我们在 override 方法中,添加了 CRUD 的计数功能。这里的代码简写为: + +1 PerformanceCounter.CountQuery\(); + +对原有代码的修改则是需要注册新的 CountableRepository\ 类型。 + +![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif) + +复制代码 + +1 Func\ factory = () =\> 2 { 3 return new UnityContainer() 4 +.ReplaceBehaviorExtensionsWithSafeExtension() 5 +.RegisterType(typeof(IRepository\<\>), typeof(Repository\<\>), new +ContainerControlledLifetimeManager()) 6 +.RegisterType(typeof(CountableRepository\<\>), new +ContainerControlledLifetimeManager()) 7 .RegisterType\(new ContainerControlledLifetimeManager()) 8 +.RegisterType\(new +ContainerControlledLifetimeManager()) 9 .RegisterType\(new +ContainerControlledLifetimeManager()) 10 .RegisterType\(new ContainerControlledLifetimeManager()) 11 +.RegisterType\(new +ContainerControlledLifetimeManager()); 12 }; + +![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif) + +复制代码 + +**扩展应用** + +既然有了抽象基类 RepositoryDecorator\ ,我们可以从其设计衍生多个特定场景的 +Repository 。 + +比如,当我们需要为某个 Table 的 Entity 添加缓存功能时,我们可以定制一个 +CachableRepository\ 来完成这一个扩展。 diff --git a/设计模式/EntityFramework用法探索(四)Repository和UnitOfWork.md b/设计模式/EntityFramework用法探索(四)Repository和UnitOfWork.md new file mode 100644 index 00000000..b7d984fe --- /dev/null +++ b/设计模式/EntityFramework用法探索(四)Repository和UnitOfWork.md @@ -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\ 2 where T : class3 { 4 IQueryable\ +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\ : IRepository\ where T : class 2 { 3 private +readonly IObjectSetFactory \_objectSetFactory; 4 private readonly +IObjectSet\ \_objectSet; 5 6 public Repository(IObjectSetFactory +objectSetFactory) 7 { 8 \_objectSetFactory = objectSetFactory; 9 \_objectSet = +objectSetFactory.CreateObjectSet\(); 10 } 11 12 \#region IRepository\ +Members 13 14 public IQueryable\ 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\(); 4 Mapper.CreateMap\(); 5 6 +DbContext context = new RETAILContext(); 7 DbContextAdapter contextAdaptor = new +DbContextAdapter(context); 8 9 IObjectSetFactory objectSetFactory = +contextAdaptor; 10 \_repository = new Repository\(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\(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 + +- (七)线程安全实践 + +- (八)事务处理 + +完整代码下载 diff --git a/设计模式/Lambda应用设计模式.md b/设计模式/Lambda应用设计模式.md new file mode 100644 index 00000000..0ec2db24 --- /dev/null +++ b/设计模式/Lambda应用设计模式.md @@ -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\ 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\ 类型。 + +![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif) + +复制代码 + +1 static class Translations 2 { 3 static readonly Dictionary\\> smnFunctions 4 = new Dictionary\\>(); 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\ 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\ 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\ 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 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 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\ +仅存储了查询而没有存储基础的数据。一旦我们需要这些数据,不仅已构造的查询会被执行,而且查询也是以最高效的形式来执行,例如在远端数据库服务器上执行 +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\\> +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\ 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\(); 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\ 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\ finalizers; 2 3 public void BuildFinalizers() 4 { +5 finalizers = new Dictionary\(); 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\ 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\(); 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\(); 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\ converter)22 { 23 Converter = converter; 24 +Expected = expected; 25 } 26 27 public ValueConverterAttribute(Type expected) 28 +{ 29 Expected = expected; 30 } 31 32 public Func\ 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\ +,实际上也是一个结构体类型。 + +终于,Lambda表达式来拯救我们了。 + +![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif) + +复制代码 + +1 struct FirstStruct 2 { 3 readonly Func\ 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\ 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\ 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\ 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\)((x) =\> 73 { 74 Console.WriteLine(2.0 \* x \* x - 0.5 \* +x); 75 }))(1.0); 76 77 78 ((Action\)((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\)((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\ 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) +**,章节和代码有很大的改动,未包含全部内容。** diff --git a/设计模式/media/026ab145cc830444e57f84c06e105192.png b/设计模式/media/026ab145cc830444e57f84c06e105192.png new file mode 100644 index 00000000..8b1e9aef Binary files /dev/null and b/设计模式/media/026ab145cc830444e57f84c06e105192.png differ diff --git a/设计模式/media/034ce7ebd324d04b21ef2ba935649ff9.png b/设计模式/media/034ce7ebd324d04b21ef2ba935649ff9.png new file mode 100644 index 00000000..889ecc35 Binary files /dev/null and b/设计模式/media/034ce7ebd324d04b21ef2ba935649ff9.png differ diff --git a/设计模式/media/0351faeada38866d0a76a2d60ff2e9b3.png b/设计模式/media/0351faeada38866d0a76a2d60ff2e9b3.png new file mode 100644 index 00000000..80da8d12 Binary files /dev/null and b/设计模式/media/0351faeada38866d0a76a2d60ff2e9b3.png differ diff --git a/设计模式/media/06f6afee0fb26038e1df96cc245c24f4.png b/设计模式/media/06f6afee0fb26038e1df96cc245c24f4.png new file mode 100644 index 00000000..9f32377f Binary files /dev/null and b/设计模式/media/06f6afee0fb26038e1df96cc245c24f4.png differ diff --git a/设计模式/media/0d3edc3fdc41b5a9178d24327f569bfc.png b/设计模式/media/0d3edc3fdc41b5a9178d24327f569bfc.png new file mode 100644 index 00000000..fd7d47af Binary files /dev/null and b/设计模式/media/0d3edc3fdc41b5a9178d24327f569bfc.png differ diff --git a/设计模式/media/0ed3006df19ccbbfe704a011872d66c4.png b/设计模式/media/0ed3006df19ccbbfe704a011872d66c4.png new file mode 100644 index 00000000..390b58cd Binary files /dev/null and b/设计模式/media/0ed3006df19ccbbfe704a011872d66c4.png differ diff --git a/设计模式/media/2270a94077e069568d686166f5101fc9.png b/设计模式/media/2270a94077e069568d686166f5101fc9.png new file mode 100644 index 00000000..02b85d40 Binary files /dev/null and b/设计模式/media/2270a94077e069568d686166f5101fc9.png differ diff --git a/设计模式/media/28cabe60fc8136de5c80e251000dd2ee.png b/设计模式/media/28cabe60fc8136de5c80e251000dd2ee.png new file mode 100644 index 00000000..8eeeafd7 Binary files /dev/null and b/设计模式/media/28cabe60fc8136de5c80e251000dd2ee.png differ diff --git a/设计模式/media/2b415d88952f47d7d4cd707cbc009287.png b/设计模式/media/2b415d88952f47d7d4cd707cbc009287.png new file mode 100644 index 00000000..137f09f7 Binary files /dev/null and b/设计模式/media/2b415d88952f47d7d4cd707cbc009287.png differ diff --git a/设计模式/media/2b90215e2319cee7220018f7e74ac572.png b/设计模式/media/2b90215e2319cee7220018f7e74ac572.png new file mode 100644 index 00000000..276bc493 Binary files /dev/null and b/设计模式/media/2b90215e2319cee7220018f7e74ac572.png differ diff --git a/设计模式/media/2e18e1a71f5bcf86ff211a4ecad48cbc.png b/设计模式/media/2e18e1a71f5bcf86ff211a4ecad48cbc.png new file mode 100644 index 00000000..b5748771 Binary files /dev/null and b/设计模式/media/2e18e1a71f5bcf86ff211a4ecad48cbc.png differ diff --git a/设计模式/media/3002203a9f9b82e63b10eafc66f0fcb3.png b/设计模式/media/3002203a9f9b82e63b10eafc66f0fcb3.png new file mode 100644 index 00000000..e3bbbaba Binary files /dev/null and b/设计模式/media/3002203a9f9b82e63b10eafc66f0fcb3.png differ diff --git a/设计模式/media/31e9acd8d4ae264b538586670f9ffed9.png b/设计模式/media/31e9acd8d4ae264b538586670f9ffed9.png new file mode 100644 index 00000000..90eda242 Binary files /dev/null and b/设计模式/media/31e9acd8d4ae264b538586670f9ffed9.png differ diff --git a/设计模式/media/322c8a847832fc5d2385537079343d7c.png b/设计模式/media/322c8a847832fc5d2385537079343d7c.png new file mode 100644 index 00000000..209d4c6a Binary files /dev/null and b/设计模式/media/322c8a847832fc5d2385537079343d7c.png differ diff --git a/设计模式/media/33fb368703c7a2f6e4146faae22ef6ad.png b/设计模式/media/33fb368703c7a2f6e4146faae22ef6ad.png new file mode 100644 index 00000000..d037e521 Binary files /dev/null and b/设计模式/media/33fb368703c7a2f6e4146faae22ef6ad.png differ diff --git a/设计模式/media/372aa1b79b9805da8ffaeb2680a2e138.png b/设计模式/media/372aa1b79b9805da8ffaeb2680a2e138.png new file mode 100644 index 00000000..a814382a Binary files /dev/null and b/设计模式/media/372aa1b79b9805da8ffaeb2680a2e138.png differ diff --git a/设计模式/media/37bc98b89e45d29981e717145b971aa0.png b/设计模式/media/37bc98b89e45d29981e717145b971aa0.png new file mode 100644 index 00000000..342c32e3 Binary files /dev/null and b/设计模式/media/37bc98b89e45d29981e717145b971aa0.png differ diff --git a/设计模式/media/3d659ede408fbd325ce99f760e43338c.png b/设计模式/media/3d659ede408fbd325ce99f760e43338c.png new file mode 100644 index 00000000..d28a8780 Binary files /dev/null and b/设计模式/media/3d659ede408fbd325ce99f760e43338c.png differ diff --git a/设计模式/media/405b18b4b6584ae338e0f6ecaf736533.gif b/设计模式/media/405b18b4b6584ae338e0f6ecaf736533.gif new file mode 100644 index 00000000..ea0552d4 Binary files /dev/null and b/设计模式/media/405b18b4b6584ae338e0f6ecaf736533.gif differ diff --git a/设计模式/media/449673a26c6c7fbe0fe391c24734c75a.png b/设计模式/media/449673a26c6c7fbe0fe391c24734c75a.png new file mode 100644 index 00000000..a68b745f Binary files /dev/null and b/设计模式/media/449673a26c6c7fbe0fe391c24734c75a.png differ diff --git a/设计模式/media/474507cff41bb4cb9eb9b309aa3f9d44.png b/设计模式/media/474507cff41bb4cb9eb9b309aa3f9d44.png new file mode 100644 index 00000000..fe1ff183 Binary files /dev/null and b/设计模式/media/474507cff41bb4cb9eb9b309aa3f9d44.png differ diff --git a/设计模式/media/4ac1f99f76e6d199ef34ae1fb3f26ba2.jpeg b/设计模式/media/4ac1f99f76e6d199ef34ae1fb3f26ba2.jpeg new file mode 100644 index 00000000..eedfac45 Binary files /dev/null and b/设计模式/media/4ac1f99f76e6d199ef34ae1fb3f26ba2.jpeg differ diff --git a/设计模式/media/51e409b11aa51c150090697429a953ed.gif b/设计模式/media/51e409b11aa51c150090697429a953ed.gif new file mode 100644 index 00000000..dc146865 Binary files /dev/null and b/设计模式/media/51e409b11aa51c150090697429a953ed.gif differ diff --git a/设计模式/media/5558a63c1bb2ff6c348c2e4ad817c713.png b/设计模式/media/5558a63c1bb2ff6c348c2e4ad817c713.png new file mode 100644 index 00000000..258349af Binary files /dev/null and b/设计模式/media/5558a63c1bb2ff6c348c2e4ad817c713.png differ diff --git a/设计模式/media/5568d552b41e34f340727f1b1d17df21.png b/设计模式/media/5568d552b41e34f340727f1b1d17df21.png new file mode 100644 index 00000000..688fe1ca Binary files /dev/null and b/设计模式/media/5568d552b41e34f340727f1b1d17df21.png differ diff --git a/设计模式/media/5870f1e66b4dba9e3f59dcd29cf0f8bd.png b/设计模式/media/5870f1e66b4dba9e3f59dcd29cf0f8bd.png new file mode 100644 index 00000000..a7f710cf Binary files /dev/null and b/设计模式/media/5870f1e66b4dba9e3f59dcd29cf0f8bd.png differ diff --git a/设计模式/media/59d8d51a3c8b682d64bde56b4b6e641f.png b/设计模式/media/59d8d51a3c8b682d64bde56b4b6e641f.png new file mode 100644 index 00000000..b23602e8 Binary files /dev/null and b/设计模式/media/59d8d51a3c8b682d64bde56b4b6e641f.png differ diff --git a/设计模式/media/5f82f2984b309bf829857bedadd73e7d.png b/设计模式/media/5f82f2984b309bf829857bedadd73e7d.png new file mode 100644 index 00000000..239c38bc Binary files /dev/null and b/设计模式/media/5f82f2984b309bf829857bedadd73e7d.png differ diff --git a/设计模式/media/62502293d8ec3c5ee0cb6130ba088ad9.png b/设计模式/media/62502293d8ec3c5ee0cb6130ba088ad9.png new file mode 100644 index 00000000..489cdfec Binary files /dev/null and b/设计模式/media/62502293d8ec3c5ee0cb6130ba088ad9.png differ diff --git a/设计模式/media/688b6a2cfb78ed1f0b1a37b98f116dc9.png b/设计模式/media/688b6a2cfb78ed1f0b1a37b98f116dc9.png new file mode 100644 index 00000000..72b0c4be Binary files /dev/null and b/设计模式/media/688b6a2cfb78ed1f0b1a37b98f116dc9.png differ diff --git a/设计模式/media/6a7e900756c3af21380c9575cb0a6941.png b/设计模式/media/6a7e900756c3af21380c9575cb0a6941.png new file mode 100644 index 00000000..888b2c7c Binary files /dev/null and b/设计模式/media/6a7e900756c3af21380c9575cb0a6941.png differ diff --git a/设计模式/media/6d9c1fed25d022e36715958e50c87e30.png b/设计模式/media/6d9c1fed25d022e36715958e50c87e30.png new file mode 100644 index 00000000..204b355b Binary files /dev/null and b/设计模式/media/6d9c1fed25d022e36715958e50c87e30.png differ diff --git a/设计模式/media/7425d22312c40949be9309f07a4f5078.png b/设计模式/media/7425d22312c40949be9309f07a4f5078.png new file mode 100644 index 00000000..3bfca0d7 Binary files /dev/null and b/设计模式/media/7425d22312c40949be9309f07a4f5078.png differ diff --git a/设计模式/media/7428e3ffa507ae62c5f8f131247eba36.png b/设计模式/media/7428e3ffa507ae62c5f8f131247eba36.png new file mode 100644 index 00000000..520e757c Binary files /dev/null and b/设计模式/media/7428e3ffa507ae62c5f8f131247eba36.png differ diff --git a/设计模式/media/7f6de13b5ce6b6c8c6ed6422472d1c32.png b/设计模式/media/7f6de13b5ce6b6c8c6ed6422472d1c32.png new file mode 100644 index 00000000..5edbd635 Binary files /dev/null and b/设计模式/media/7f6de13b5ce6b6c8c6ed6422472d1c32.png differ diff --git a/设计模式/media/87dce300b403ff117d52bd429afb026d.png b/设计模式/media/87dce300b403ff117d52bd429afb026d.png new file mode 100644 index 00000000..e0cd7fd0 Binary files /dev/null and b/设计模式/media/87dce300b403ff117d52bd429afb026d.png differ diff --git a/设计模式/media/8e59380941dd441fd6cea190c39f4520.png b/设计模式/media/8e59380941dd441fd6cea190c39f4520.png new file mode 100644 index 00000000..77d8a2ee Binary files /dev/null and b/设计模式/media/8e59380941dd441fd6cea190c39f4520.png differ diff --git a/设计模式/media/9bae3ff976c8470b1d6ae9e9768409e4.png b/设计模式/media/9bae3ff976c8470b1d6ae9e9768409e4.png new file mode 100644 index 00000000..df20388a Binary files /dev/null and b/设计模式/media/9bae3ff976c8470b1d6ae9e9768409e4.png differ diff --git a/设计模式/media/9e12cba8d2bed321f8e089e839e244ad.png b/设计模式/media/9e12cba8d2bed321f8e089e839e244ad.png new file mode 100644 index 00000000..8a9ff282 Binary files /dev/null and b/设计模式/media/9e12cba8d2bed321f8e089e839e244ad.png differ diff --git a/设计模式/media/9fba7d170b67e64fbefe8a4822465601.png b/设计模式/media/9fba7d170b67e64fbefe8a4822465601.png new file mode 100644 index 00000000..59ff2579 Binary files /dev/null and b/设计模式/media/9fba7d170b67e64fbefe8a4822465601.png differ diff --git a/设计模式/media/a5922a22f70dce34455e19954aeac363.png b/设计模式/media/a5922a22f70dce34455e19954aeac363.png new file mode 100644 index 00000000..41db4786 Binary files /dev/null and b/设计模式/media/a5922a22f70dce34455e19954aeac363.png differ diff --git a/设计模式/media/a6c50fe1fcfff1a376d34e0af97ace54.png b/设计模式/media/a6c50fe1fcfff1a376d34e0af97ace54.png new file mode 100644 index 00000000..e792f69b Binary files /dev/null and b/设计模式/media/a6c50fe1fcfff1a376d34e0af97ace54.png differ diff --git a/设计模式/media/a8d3ed7b8538ad1d4b557cce8f56bc83.png b/设计模式/media/a8d3ed7b8538ad1d4b557cce8f56bc83.png new file mode 100644 index 00000000..3681a22c Binary files /dev/null and b/设计模式/media/a8d3ed7b8538ad1d4b557cce8f56bc83.png differ diff --git a/设计模式/media/a963ba27b48428ef5a370d945ce1e679.jpeg b/设计模式/media/a963ba27b48428ef5a370d945ce1e679.jpeg new file mode 100644 index 00000000..713e9ed8 Binary files /dev/null and b/设计模式/media/a963ba27b48428ef5a370d945ce1e679.jpeg differ diff --git a/设计模式/media/abc2df6bb6578e6901711922fb704575.png b/设计模式/media/abc2df6bb6578e6901711922fb704575.png new file mode 100644 index 00000000..6569c0a7 Binary files /dev/null and b/设计模式/media/abc2df6bb6578e6901711922fb704575.png differ diff --git a/设计模式/media/c03017c41ac99aa131b7e675f1022124.png b/设计模式/media/c03017c41ac99aa131b7e675f1022124.png new file mode 100644 index 00000000..2e40023c Binary files /dev/null and b/设计模式/media/c03017c41ac99aa131b7e675f1022124.png differ diff --git a/设计模式/media/c13421b53c7c2b2146d2198f7ac4f85b.png b/设计模式/media/c13421b53c7c2b2146d2198f7ac4f85b.png new file mode 100644 index 00000000..88717666 Binary files /dev/null and b/设计模式/media/c13421b53c7c2b2146d2198f7ac4f85b.png differ diff --git a/设计模式/media/d757c7f18fd48bc5989ba6d1fb01b253.png b/设计模式/media/d757c7f18fd48bc5989ba6d1fb01b253.png new file mode 100644 index 00000000..70236089 Binary files /dev/null and b/设计模式/media/d757c7f18fd48bc5989ba6d1fb01b253.png differ diff --git a/设计模式/media/d95ed5b46b2cab7212804a212475ebef.png b/设计模式/media/d95ed5b46b2cab7212804a212475ebef.png new file mode 100644 index 00000000..19508226 Binary files /dev/null and b/设计模式/media/d95ed5b46b2cab7212804a212475ebef.png differ diff --git a/设计模式/media/dd67bc16239c21383f68dac90175918b.png b/设计模式/media/dd67bc16239c21383f68dac90175918b.png new file mode 100644 index 00000000..48010f51 Binary files /dev/null and b/设计模式/media/dd67bc16239c21383f68dac90175918b.png differ diff --git a/设计模式/media/de271ae3dd89c9f862ae1b16714ff5db.png b/设计模式/media/de271ae3dd89c9f862ae1b16714ff5db.png new file mode 100644 index 00000000..9c931d8a Binary files /dev/null and b/设计模式/media/de271ae3dd89c9f862ae1b16714ff5db.png differ diff --git a/设计模式/media/e2021eb969b6e2e1521577e9d395b462.png b/设计模式/media/e2021eb969b6e2e1521577e9d395b462.png new file mode 100644 index 00000000..3e1706f5 Binary files /dev/null and b/设计模式/media/e2021eb969b6e2e1521577e9d395b462.png differ diff --git a/设计模式/media/e34e3723a1d1f114c83da1351b80724a.jpeg b/设计模式/media/e34e3723a1d1f114c83da1351b80724a.jpeg new file mode 100644 index 00000000..898f5046 Binary files /dev/null and b/设计模式/media/e34e3723a1d1f114c83da1351b80724a.jpeg differ diff --git a/设计模式/media/e60068b39c1312cbb4ad787dc949202f.png b/设计模式/media/e60068b39c1312cbb4ad787dc949202f.png new file mode 100644 index 00000000..91e975d5 Binary files /dev/null and b/设计模式/media/e60068b39c1312cbb4ad787dc949202f.png differ diff --git a/设计模式/media/e9be1328d4c07a358ba694abb1297b65.png b/设计模式/media/e9be1328d4c07a358ba694abb1297b65.png new file mode 100644 index 00000000..42ad92cf Binary files /dev/null and b/设计模式/media/e9be1328d4c07a358ba694abb1297b65.png differ diff --git a/设计模式/media/eaa51861819d82c88eaf50c6924776a9.png b/设计模式/media/eaa51861819d82c88eaf50c6924776a9.png new file mode 100644 index 00000000..7fb4a9ad Binary files /dev/null and b/设计模式/media/eaa51861819d82c88eaf50c6924776a9.png differ diff --git a/设计模式/media/ebed27694b48db7da45d4f8a573d1001.png b/设计模式/media/ebed27694b48db7da45d4f8a573d1001.png new file mode 100644 index 00000000..0580153c Binary files /dev/null and b/设计模式/media/ebed27694b48db7da45d4f8a573d1001.png differ diff --git a/设计模式/media/ee4a265f47725eacf53741680d262704.png b/设计模式/media/ee4a265f47725eacf53741680d262704.png new file mode 100644 index 00000000..3abc87bb Binary files /dev/null and b/设计模式/media/ee4a265f47725eacf53741680d262704.png differ diff --git a/设计模式/media/f2793f123ffd3e6517ffca83c3b4218e.png b/设计模式/media/f2793f123ffd3e6517ffca83c3b4218e.png new file mode 100644 index 00000000..b4d9656e Binary files /dev/null and b/设计模式/media/f2793f123ffd3e6517ffca83c3b4218e.png differ diff --git a/设计模式/media/f8bea867abcf60fc1549e6ecf2748c58.png b/设计模式/media/f8bea867abcf60fc1549e6ecf2748c58.png new file mode 100644 index 00000000..e3c2a1b9 Binary files /dev/null and b/设计模式/media/f8bea867abcf60fc1549e6ecf2748c58.png differ diff --git a/设计模式/media/fa6446797ee1d21a2cdf19e23439008a.png b/设计模式/media/fa6446797ee1d21a2cdf19e23439008a.png new file mode 100644 index 00000000..e1957b03 Binary files /dev/null and b/设计模式/media/fa6446797ee1d21a2cdf19e23439008a.png differ diff --git a/设计模式/单一职责原则(Single Responsibility Principle).md b/设计模式/单一职责原则(Single Responsibility Principle).md new file mode 100644 index 00000000..e6d09b5f --- /dev/null +++ b/设计模式/单一职责原则(Single Responsibility Principle).md @@ -0,0 +1,183 @@ +单一职责原则(Single Responsibility Principle) + +**单一职责原则(SRP:The 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)到底是什么?** + +**在**[**单一职责原则(SRP:Single 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 +仅在被实例化的位置才会出现。我们将丑陋的代码限制在一定的范围内,而不会泄露或污染应用程序的其他部分。 + +**总结** + +单一职责原则(SRP:Single 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) | + +**参考资料** + +- *SRP:The 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* diff --git a/设计模式/开放封闭原则(Open Closed Principle).md b/设计模式/开放封闭原则(Open Closed Principle).md new file mode 100644 index 00000000..5decb642 --- /dev/null +++ b/设计模式/开放封闭原则(Open Closed Principle).md @@ -0,0 +1,280 @@ +开放封闭原则(Open Closed Principle) + +在面向对象的设计中有很多流行的思想,比如说 +"所有的成员变量都应该设置为私有(Private)","要避免使用全局变量(Global +Variables)","使用运行时类型识别(RTTI:Run 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\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\ 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\ 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\ shapes) 41 { +42 SortedSet\ orderedList = 43 new SortedSet\(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\ \_typeOrderTable = new Dictionary\(); 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) | + +**参考资料** + +- *OCP:The 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* diff --git a/设计模式/最少知识原则(Least Knowledge Principle).md b/设计模式/最少知识原则(Least Knowledge Principle).md new file mode 100644 index 00000000..5251b99c --- /dev/null +++ b/设计模式/最少知识原则(Least Knowledge Principle).md @@ -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.** + +改进后的原则称为 LoDC(Law of Demeter for +Concerns),它为软件设计带来了两个主要的益处: + +> It leads to better information hiding. + +> It leads to less information overload. + +即,**更好的信息隐藏和更少的信息重载**。LoDC +原则在面向方面的软件开发(AOSD:Aspect-Oriented Software +Development)中有着良好的应用。 + +**最少知识原则在面向对象编程中的应用** + +在 "Law of Demeter" 应用于面向对象编程中时,可以简称为 "LoD-F:Law 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) | diff --git a/设计模式/设计模式之美:Abstract Factory(抽象工厂).md b/设计模式/设计模式之美:Abstract Factory(抽象工厂).md new file mode 100644 index 00000000..8d86336e --- /dev/null +++ b/设计模式/设计模式之美:Abstract Factory(抽象工厂).md @@ -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\() 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\ : 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\() 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\(); 60 AbstractOrInterfaceOfProductA productA = +kit.CreateProductA(); 61 AbstractOrInterfaceOfProductB productB = +kit.CreateProductB(); 62 AbstractOrInterfaceOfProductC productC = +kit.CreateProductC\(); 63 } 64 } 65 } + +![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif) + +复制代码 diff --git a/设计模式/设计模式之美:Adapter(适配器).md b/设计模式/设计模式之美:Adapter(适配器).md new file mode 100644 index 00000000..60af5e7b --- /dev/null +++ b/设计模式/设计模式之美:Adapter(适配器).md @@ -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) + +复制代码 diff --git a/设计模式/设计模式之美:Behavioral Patterns(行为型模式).md b/设计模式/设计模式之美:Behavioral Patterns(行为型模式).md new file mode 100644 index 00000000..64cea2b9 --- /dev/null +++ b/设计模式/设计模式之美:Behavioral Patterns(行为型模式).md @@ -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 +代表一个对象在某个时刻的内部状态。 diff --git a/设计模式/设计模式之美:Bridge(桥接).md b/设计模式/设计模式之美:Bridge(桥接).md new file mode 100644 index 00000000..b85cc432 --- /dev/null +++ b/设计模式/设计模式之美:Bridge(桥接).md @@ -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) + +复制代码 diff --git a/设计模式/设计模式之美:Builder(生成器).md b/设计模式/设计模式之美:Builder(生成器).md new file mode 100644 index 00000000..8e6fdc35 --- /dev/null +++ b/设计模式/设计模式之美:Builder(生成器).md @@ -0,0 +1,199 @@ +设计模式之美:Builder(生成器) + +**索引** + +- 意图 + +- 结构 + +- 参与者 + +- 适用性 + +- 效果 + +- 相关模式 + +- 实现 + + - 实现方式(一):Builder 为每个构件定义一个操作。 + + - 实现方式(二):Builder 将构件返回给 Director,Director 将构件传递给 + 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 将构件返回给 Director,Director 将构件传递给 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) + +复制代码 diff --git a/设计模式/设计模式之美:Chain of Responsibility(职责链).md b/设计模式/设计模式之美:Chain of Responsibility(职责链).md new file mode 100644 index 00000000..b5145cbe --- /dev/null +++ b/设计模式/设计模式之美:Chain of Responsibility(职责链).md @@ -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) + +复制代码 diff --git a/设计模式/设计模式之美:Command(命令).md b/设计模式/设计模式之美:Command(命令).md new file mode 100644 index 00000000..844a970d --- /dev/null +++ b/设计模式/设计模式之美:Command(命令).md @@ -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\ \_action;11 private string \_state; 12 +13 public ConcreteCommand(Action\ 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\ +: Command 9 { 10 private Action\ \_action; 11 private T \_state1; 12 +private S \_state2; 13 14 public ConcreteCommand(Action\ 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\(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\ \_cmdList = new List\(); 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 } diff --git a/设计模式/设计模式之美:Composite(组合).md b/设计模式/设计模式之美:Composite(组合).md new file mode 100644 index 00000000..6c2c9b9e --- /dev/null +++ b/设计模式/设计模式之美:Composite(组合).md @@ -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\ \_children = new +List\(); 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\ +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\ 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) + +复制代码 diff --git a/设计模式/设计模式之美:Creational Patterns(创建型模式).md b/设计模式/设计模式之美:Creational Patterns(创建型模式).md new file mode 100644 index 00000000..411ecb44 --- /dev/null +++ b/设计模式/设计模式之美:Creational Patterns(创建型模式).md @@ -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 +开始,并且当设计者发现需要更大的灵活性时,设计便会向其他创建型模式演化。** diff --git a/设计模式/设计模式之美:Decorator(装饰).md b/设计模式/设计模式之美:Decorator(装饰).md new file mode 100644 index 00000000..5f91f631 --- /dev/null +++ b/设计模式/设计模式之美:Decorator(装饰).md @@ -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) + +复制代码 diff --git a/设计模式/设计模式之美:Dynamic Property(动态属性).md b/设计模式/设计模式之美:Dynamic Property(动态属性).md new file mode 100644 index 00000000..33edcd02 --- /dev/null +++ b/设计模式/设计模式之美:Dynamic Property(动态属性).md @@ -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\ 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\ \_properties 43 = new Dictionary\(); 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\ +GetAllProperties() 84 { 85 List\ properties = new List\(); +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 } diff --git a/设计模式/设计模式之美:Extension Object(扩展对象).md b/设计模式/设计模式之美:Extension Object(扩展对象).md new file mode 100644 index 00000000..77e6e73d --- /dev/null +++ b/设计模式/设计模式之美:Extension Object(扩展对象).md @@ -0,0 +1,153 @@ +**索引** + +- 意图 + +- 结构 + +- 参与者 + +- 适用性 + +- 效果 + +- 相关模式 + +- 实现 + + - 实现方式(一):使用示例结构实现 Extension Object。 + + - 实现方式(二):使用泛型实现 IExtensibleObject\ 接口。 + +**意图** + +预期对象的接口将在未来被扩展。通过额外的接口来定义扩展对象。 + +Anticipate that an object’s 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\ 接口。** + +![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif) + +复制代码 + +1 namespace ExtensionObjectPattern.Implementation2 2 { 3 public abstract class +Subject : IExtensibleObject\ 4 { 5 public abstract void Operation1(); +6 public abstract IEnumerable\\> Extensions { get; } 7 } 8 +9 public interface IExtensibleObject\ 10 where T : class, +IExtensibleObject\11 { 12 IEnumerable\\> Extensions { get; +}13 } 14 15 public interface IExtension\ 16 where T : class, +IExtensibleObject\17 { 18 void Attach(T owner); 19 void Detach(T owner); 20 +} 21 22 public abstract class Extension : IExtension\ 23 { 24 +protected List\ \_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\ \_extensions = new List\(); 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\\> 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 } diff --git a/设计模式/设计模式之美:Facade(外观).md b/设计模式/设计模式之美:Facade(外观).md new file mode 100644 index 00000000..7d47c3dc --- /dev/null +++ b/设计模式/设计模式之美:Facade(外观).md @@ -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) + +复制代码 diff --git a/设计模式/设计模式之美:Factory Method(工厂方法).md b/设计模式/设计模式之美:Factory Method(工厂方法).md new file mode 100644 index 00000000..169ff4df --- /dev/null +++ b/设计模式/设计模式之美:Factory Method(工厂方法).md @@ -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\ : +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\(); 34 AbstractOrInterfaceOfCreator +creator2 = new GenericConcreteCreator\(); 35 +AbstractOrInterfaceOfProduct product1 = creator1.CreateProduct(); 36 +AbstractOrInterfaceOfProduct product2 = creator2.CreateProduct(); 37 } 38 } 39 } + +![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif) + +复制代码 diff --git a/设计模式/设计模式之美:Flyweight(享元).md b/设计模式/设计模式之美:Flyweight(享元).md new file mode 100644 index 00000000..4d17e1d0 --- /dev/null +++ b/设计模式/设计模式之美:Flyweight(享元).md @@ -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\ \_pool25 = new Dictionary\();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 } diff --git a/设计模式/设计模式之美:Interpreter(解释器).md b/设计模式/设计模式之美:Interpreter(解释器).md new file mode 100644 index 00000000..013a478a --- /dev/null +++ b/设计模式/设计模式之美:Interpreter(解释器).md @@ -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:聚合一到多个 Expression,Expression 可以是 +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\(polish.Split(' ')); 90 return +ParseNextExpression(symbols); 91 } 92 93 private IExpression +ParseNextExpression(List\ 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\ 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) + +复制代码 diff --git a/设计模式/设计模式之美:Iterator(迭代器).md b/设计模式/设计模式之美:Iterator(迭代器).md new file mode 100644 index 00000000..c53705e3 --- /dev/null +++ b/设计模式/设计模式之美:Iterator(迭代器).md @@ -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 /// \ 2 /// 二叉树节点 3 /// \ 4 /// \The item type\ 5 public class BinaryTreeNode\ 6 { 7 +\#region Constructors 8 9 /// \ 10 /// 二叉树节点 11 /// \ +12 public BinaryTreeNode() 13 { 14 } 15 16 /// \ 17 /// 二叉树节点 18 +/// \ 19 /// \节点中的值\ 20 public +BinaryTreeNode(T value) 21 { 22 this.Value = value; 23 } 24 25 /// \ +26 /// 二叉树节点 27 /// \ 28 /// \节点中的值\ 29 /// \节点的父节点\ 30 public BinaryTreeNode(T value, +BinaryTreeNode\ parent) 31 { 32 this.Value = value; 33 this.Parent = parent; +34 } 35 36 /// \ 37 /// 二叉树节点 38 /// \ 39 /// \节点中的值\ 40 /// \节点的父节点\ 41 /// \节点的左节点\ 42 /// \节点的右节点\ 43 public BinaryTreeNode(T value, 44 +BinaryTreeNode\ parent, 45 BinaryTreeNode\ left, 46 BinaryTreeNode\ +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 +/// \ 59 /// 节点值 60 /// \ 61 public T Value { get; set; +} 62 63 /// \ 64 /// 父节点 65 /// \ 66 public +BinaryTreeNode\ Parent { get; set; } 67 68 /// \ 69 /// 左节点 70 +/// \ 71 public BinaryTreeNode\ Left { get; set; } 72 73 /// +\ 74 /// 右节点 75 /// \ 76 public BinaryTreeNode\ +Right { get; set; } 77 78 /// \ 79 /// 是否为根节点 80 /// +\ 81 public bool IsRoot { get { return Parent == null; } } 82 83 /// +\ 84 /// 是否为叶子节点 85 /// \ 86 public bool IsLeaf { +get { return Left == null && Right == null; } } 87 88 /// \ 89 /// +是否为可访问的 90 /// \ 91 internal bool Visited { get; set; } 92 93 +\#endregion 94 95 \#region Public Overridden Functions 96 97 /// \ 98 +/// Returns a \ that represents this instance. 99 +/// \100 /// \101 /// A \ that +represents this instance. 102 /// \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 /// \ 2 /// 二叉树 3 /// \ 4 /// \二叉树中节点内容类型\ 5 +[SuppressMessage("Microsoft.Naming", +"CA1710:IdentifiersShouldHaveCorrectSuffix")] 6 public class BinaryTree\ : +ICollection\, IEnumerable\ where T : IComparable\ 7 { 8 \#region +Constructor 9 10 /// \ 11 /// 二叉树 12 /// \ 13 public +BinaryTree() 14 { 15 NumberOfNodes = 0; 16 } 17 18 /// \ 19 /// 二叉树 +20 /// \ 21 /// \二叉树根节点\ 22 public +BinaryTree(BinaryTreeNode\ root) 23 : this() 24 { 25 this.Root = root; 26 } +27 28 \#endregion 29 30 \#region Properties 31 32 /// \ 33 /// +树的根节点 34 /// \ 35 public BinaryTreeNode\ Root { get; set; } +36 37 /// \ 38 /// 树中节点的数量 39 /// \ 40 protected int +NumberOfNodes { get; set; } 41 42 /// \ 43 /// 树是否为空 44 /// +\ 45 public bool IsEmpty { get { return Root == null; } } 46 47 /// +\ 48 /// 获取树中节点的最小值 49 /// \ 50 public T MinValue +51 { 52 get 53 { 54 if (IsEmpty) 55 return default(T); 56 57 BinaryTreeNode\ +minNode = Root; 58 while (minNode.Left != null) 59 minNode = minNode.Left; 60 61 +return minNode.Value; 62 } 63 } 64 65 /// \ 66 /// +获取树中节点的最大值 67 /// \ 68 public T MaxValue 69 { 70 get 71 { +72 if (IsEmpty) 73 return default(T); 74 75 BinaryTreeNode\ 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\ +Members 86 87 /// \ 88 /// Returns an enumerator that iterates through +the collection. 89 /// \ 90 /// \ 91 /// A \\ 92 /// that can be +used to iterate through the collection. 93 /// \ 94 public +IEnumerator\ GetEnumerator() 95 { 96 foreach (BinaryTreeNode\ node in +Traverse(Root)) 97 { 98 yield return node.Value; 99 } 100 } 101 102 +\#endregion103 104 \#region IEnumerable Members 105 106 /// \107 /// +Returns an enumerator that iterates through a collection. 108 /// +\109 /// \110 /// An \ 111 /// object that can be used to +iterate through the collection. 112 /// \113 IEnumerator +IEnumerable.GetEnumerator() 114 { 115 foreach (BinaryTreeNode\ node in +Traverse(Root)) 116 { 117 yield return node.Value; 118 } 119 } 120 121 +\#endregion122 123 \#region ICollection\ Members 124 125 /// \126 +/// 新增节点 127 /// \128 /// \The object to add +to the129 /// \\.\130 /// +\The131 /// \\ 132 /// is +read-only.\133 public void Add(T item) 134 { 135 if (Root == +null)136 { 137 Root = new BinaryTreeNode\(item); 138 ++NumberOfNodes; 139 } +140 else141 { 142 Insert(item); 143 } 144 } 145 146 /// \147 /// +清除树 148 /// \149 public void Clear() 150 { 151 Root = null;152 } +153 154 /// \155 /// 树中是否包含此节点 156 /// \157 /// +\The object to locate in the158 /// \\.\159 /// +\160 /// true if item is found in the 161 /// \\; otherwise, +false.162 /// \163 public bool Contains(T item) 164 { 165 if +(IsEmpty) 166 return false;167 168 BinaryTreeNode\ 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 /// \184 /// 将树中节点拷贝至目标数组 185 /// \186 /// +\The array.\187 /// \Index of the array.\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 /// \201 /// 树中节点的数量 202 /// \203 public +int Count 204 { 205 get { return NumberOfNodes; } 206 } 207 208 /// +\209 /// 树是否为只读 210 /// \211 public bool IsReadOnly +212 { 213 get { return false; }214 } 215 216 /// \217 /// 移除节点 218 +/// \219 /// \节点值\220 /// +\是否移除成功\221 public bool Remove(T item) 222 { 223 +BinaryTreeNode\ node = Find(item); 224 if (node == null)225 return false;226 +227 List\ values = new List\(); 228 foreach (BinaryTreeNode\ l in +Traverse(node.Left)) 229 { 230 values.Add(l.Value); 231 } 232 foreach +(BinaryTreeNode\ 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 +/// \261 /// 查找指定值的节点 262 /// \263 /// \指定值\264 /// \265 /// 指定值的节点 266 /// +\267 protected BinaryTreeNode\ Find(T value) 268 { 269 foreach +(BinaryTreeNode\ node in Traverse(Root)) 270 if (node.Value.Equals(value)) +271 return node; 272 return null;273 } 274 275 /// \276 /// 遍历树 277 +/// \278 /// \遍历搜索的起始节点\279 /// +\280 /// The individual items from the tree 281 /// \282 +[SuppressMessage("Microsoft.Design", +"CA1006:DoNotNestGenericTypesInMemberSignatures")]283 protected +IEnumerable\\> Traverse(BinaryTreeNode\ node) 284 { 285 +// 遍历左子树286 if (node.Left != null)287 { 288 foreach (BinaryTreeNode\ +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\ +right in Traverse(node.Right)) 299 yield return right; 300 } 301 } 302 303 /// +\304 /// 插入节点 305 /// \306 /// \插入的节点值\307 protected void Insert(T value) 308 { 309 +// 从根节点开始比较310 BinaryTreeNode\ 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\(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\(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 /// \ 4 /// 双值对 5 +/// \ 6 /// \第一个值的类型\ 7 +/// \第二个值的类型\ 8 [Serializable] 9 +public struct FirstSecondPair\ 10 { 11 private TFirst first; +12 private TSecond second; 13 14 /// \ 15 /// 第一个值 16 /// +\ 17 public TFirst First 18 { 19 get 20 { 21 return this.first; 22 } +23 } 24 25 /// \ 26 /// 第二个值 27 /// \ 28 public TSecond +Second 29 { 30 get 31 { 32 return this.second; 33 } 34 } 35 36 /// \ +37 /// 双值对 38 /// \ 39 /// \第一个值\ 40 /// \第二个值\ 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 /// \ 53 /// Determines whether the specified \ is equal to this instance. 54 /// \ 55 /// +\The \ to compare with this +instance.\ 56 /// \ 57 /// \true\ if the specified +\ is equal to this instance; otherwise, +\false\. 58 /// \ 59 public override bool Equals(object obj) +60 { 61 if (obj == null) 62 return false; 63 64 FirstSecondPair\ target = (FirstSecondPair\)obj; 65 return +this.First.Equals(target.First) && this.Second.Equals(target.Second); 66 } 67 68 +/// \ 69 /// Returns a hash code for this instance. 70 /// +\ 71 /// \ 72 /// A hash code for this instance, suitable +for use in hashing algorithms and data structures like a hash table. 73 /// +\ 74 public override int GetHashCode() 75 { 76 return +base.GetHashCode(); 77 } 78 79 /// \ 80 /// Returns a \ that represents this instance. 81 /// \ 82 +/// \ 83 /// A \ that represents this +instance. 84 /// \ 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 /// +\108 /// Implements the operator ==. 109 /// \110 /// +\The left.\111 /// \The +right.\112 /// \113 /// The result of the operator. 114 /// +\115 public static bool operator ==(FirstSecondPair\ left, FirstSecondPair\ right) 116 { 117 if +(((object)left == null) \|\| ((object)right == null))118 { 119 return false;120 +} 121 122 return left.Equals(right); 123 } 124 125 /// \126 /// +Implements the operator !=. 127 /// \128 /// \The +left.\129 /// \The right.\130 /// +\131 /// The result of the operator. 132 /// \133 public +static bool operator !=(FirstSecondPair\ left, +FirstSecondPair\ right) 134 { 135 return !(left == right); 136 +} 137 } 138 139 public class BidirectionalConcurrentDictionary\ : IEnumerable\\> 140 { 141 \#region +Fields 142 143 private ConcurrentDictionary\ firstToSecond = +new ConcurrentDictionary\(); 144 private +ConcurrentDictionary\ secondToFirst = new +ConcurrentDictionary\(); 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\\> Members 263 264 +IEnumerator\\> +IEnumerable\\>.GetEnumerator() 265 { 266 +foreach (var item in firstToSecond) 267 { 268 yield return new +FirstSecondPair\(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\(item.Key, item.Value); +281 } 282 } 283 284 \#endregion285 } 286 287 public static class +ConcurrentDictionaryExtensions 288 { 289 public static TValue Add\(this ConcurrentDictionary\ 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\(this ConcurrentDictionary\ 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\(this ConcurrentDictionary\ +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\(this ConcurrentDictionary\ +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 /// \ 4 /// 循环列表 +5 /// \ 6 /// \\ 7 public class +RoundRobinCollection\ : IEnumerable\ 8 { 9 private T[] \_items; 10 +private int \_head; 11 12 /// \13 /// 循环列表 14 /// \15 +/// \供循环的列表项\16 public +RoundRobinCollection(IEnumerable\ items) 17 { 18 if (items == null \|\| +items.Count\() == 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 /// \30 /// 获取循环器 31 /// +\32 /// \\33 public IEnumerator\ +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 /// +\62 /// 获取循环器 63 /// \64 /// \\65 +IEnumerator IEnumerable.GetEnumerator() 66 { 67 return this.GetEnumerator();68 } +69 } 70 } + +![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif) + +复制代码 diff --git a/设计模式/设计模式之美:Manager(管理器).md b/设计模式/设计模式之美:Manager(管理器).md new file mode 100644 index 00000000..55eea7a5 --- /dev/null +++ b/设计模式/设计模式之美:Manager(管理器).md @@ -0,0 +1,105 @@ +**索引** + +- 意图 + +- 结构 + +- 参与者 + +- 适用性 + +- 效果 + +- 实现 + + - 实现方式(一):Manager 模式的示例实现。 + +**意图** + +将对一个类的所有对象的管理封装到一个单独的管理器类中。 + +这使得管理职责的变化独立于类本身,并且管理器还可以为不同的类进行重用。 + +Encapsulates management of a class’s objects into a separate manager object. + +This allows variation of management functionality independent of the class and +the manager’s 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\ \_books28 = new Dictionary\();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\ +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 } + +复制代码 diff --git a/设计模式/设计模式之美:Mediator(中介者).md b/设计模式/设计模式之美:Mediator(中介者).md new file mode 100644 index 00000000..db59b9f5 --- /dev/null +++ b/设计模式/设计模式之美:Mediator(中介者).md @@ -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 } + +复制代码 diff --git a/设计模式/设计模式之美:Memento(备忘录).md b/设计模式/设计模式之美:Memento(备忘录).md new file mode 100644 index 00000000..e0b882f1 --- /dev/null +++ b/设计模式/设计模式之美:Memento(备忘录).md @@ -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) + +复制代码 diff --git a/设计模式/设计模式之美:Null Object(空对象).md b/设计模式/设计模式之美:Null Object(空对象).md new file mode 100644 index 00000000..016ce3da --- /dev/null +++ b/设计模式/设计模式之美:Null Object(空对象).md @@ -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 } diff --git a/设计模式/设计模式之美:Object Pool(对象池).md b/设计模式/设计模式之美:Object Pool(对象池).md new file mode 100644 index 00000000..00e0d4d1 --- /dev/null +++ b/设计模式/设计模式之美:Object Pool(对象池).md @@ -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\ 4 { 5 private TimeSpan \_expirationTime; 6 private +Dictionary\ \_unlocked; 7 private Dictionary\ +\_locked; 8 private readonly object \_sync = new object(); 9 10 public +ObjectPool() 11 { 12 \_expirationTime = TimeSpan.FromSeconds(30); 13 \_locked = +new Dictionary\(); 14 \_unlocked = new Dictionary\(); 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\ +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 /// \ 4 /// 对象池 +5 /// \ 6 /// \对象类型\ 7 public +class ObjectPool\ where T : class 8 { 9 private readonly Func\ +\_objectFactory; 10 private readonly ConcurrentQueue\ \_queue = new +ConcurrentQueue\(); 11 12 /// \13 /// 对象池 14 /// \15 +/// \构造缓存对象的函数\16 public +ObjectPool(Func\ objectFactory) 17 { 18 \_objectFactory = objectFactory; 19 +} 20 21 /// \22 /// 构造指定数量的对象 23 /// \24 /// +\数量\25 public void Allocate(int count) 26 { 27 +for (int i = 0; i \< count; i++)28 \_queue.Enqueue(\_objectFactory()); 29 } 30 +31 /// \32 /// 缓存一个对象 33 /// \34 /// \对象\35 public void Enqueue(T obj) 36 { 37 +\_queue.Enqueue(obj); 38 } 39 40 /// \41 /// 获取一个对象 42 /// +\43 /// \对象\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\(() =\> 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) + +复制代码 diff --git a/设计模式/设计模式之美:Observer(观察者).md b/设计模式/设计模式之美:Observer(观察者).md new file mode 100644 index 00000000..00de7c01 --- /dev/null +++ b/设计模式/设计模式之美:Observer(观察者).md @@ -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\ \_observers = new List\(); 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) + +复制代码 diff --git a/设计模式/设计模式之美:Private Class Data(私有类数据).md b/设计模式/设计模式之美:Private Class Data(私有类数据).md new file mode 100644 index 00000000..4013f9d4 --- /dev/null +++ b/设计模式/设计模式之美:Private Class Data(私有类数据).md @@ -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 } diff --git a/设计模式/设计模式之美:Product Trader(操盘手).md b/设计模式/设计模式之美:Product Trader(操盘手).md new file mode 100644 index 00000000..e566b81b --- /dev/null +++ b/设计模式/设计模式之美:Product Trader(操盘手).md @@ -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\ \_dict +76 = new Dictionary\(); 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 } diff --git a/设计模式/设计模式之美:Prototype(原型).md b/设计模式/设计模式之美:Prototype(原型).md new file mode 100644 index 00000000..cd06f099 --- /dev/null +++ b/设计模式/设计模式之美:Prototype(原型).md @@ -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\ \_registry35 = new +Dictionary\();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) + +复制代码 diff --git a/设计模式/设计模式之美:Proxy(代理).md b/设计模式/设计模式之美:Proxy(代理).md new file mode 100644 index 00000000..32f28b7b --- /dev/null +++ b/设计模式/设计模式之美:Proxy(代理).md @@ -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) + +复制代码 diff --git a/设计模式/设计模式之美:Role Object(角色对象).md b/设计模式/设计模式之美:Role Object(角色对象).md new file mode 100644 index 00000000..c3b17a71 --- /dev/null +++ b/设计模式/设计模式之美:Role Object(角色对象).md @@ -0,0 +1,150 @@ +**索引** + +- 意图 + +- 结构 + +- 参与者 + +- 适用性 + +- 效果 + +- 相关模式 + +- 实现 + + - 实现方式(一):Role Object 的示例实现。 + +**意图** + +通过明确地附加角色对象到目标对象中,以使对象可以适配不同的客户需求。每个角色对象都代表着目标对象在客户上下文中的一种角色。每种上下文都存在于特定的应用程序中,因此可使对象在不同的应用程序间解耦。 + +Adapt an object to different client’s needs through transparently attached role +objects, each one representing a role the object has to play in that client’s +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\ \_roles 15 = new Dictionary\(); 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 } diff --git a/设计模式/设计模式之美:Singleton(单件).md b/设计模式/设计模式之美:Singleton(单件).md new file mode 100644 index 00000000..2e582400 --- /dev/null +++ b/设计模式/设计模式之美:Singleton(单件).md @@ -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\ \_registry 6 = new +Dictionary\(); 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) + +复制代码 diff --git a/设计模式/设计模式之美:State(状态).md b/设计模式/设计模式之美:State(状态).md new file mode 100644 index 00000000..fef425c5 --- /dev/null +++ b/设计模式/设计模式之美:State(状态).md @@ -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) + +复制代码 diff --git a/设计模式/设计模式之美:Strategy(策略).md b/设计模式/设计模式之美:Strategy(策略).md new file mode 100644 index 00000000..54a2c0cb --- /dev/null +++ b/设计模式/设计模式之美:Strategy(策略).md @@ -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) + +复制代码 diff --git a/设计模式/设计模式之美:Structural Patterns(结构型模式).md b/设计模式/设计模式之美:Structural Patterns(结构型模式).md new file mode 100644 index 00000000..47616299 --- /dev/null +++ b/设计模式/设计模式之美:Structural Patterns(结构型模式).md @@ -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 负责完成其他功能。 diff --git a/设计模式/设计模式之美:Template Method(模板方法).md b/设计模式/设计模式之美:Template Method(模板方法).md new file mode 100644 index 00000000..a5ff69a5 --- /dev/null +++ b/设计模式/设计模式之美:Template Method(模板方法).md @@ -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) + +复制代码 diff --git a/设计模式/设计模式之美:Type Object(类型对象).md b/设计模式/设计模式之美:Type Object(类型对象).md new file mode 100644 index 00000000..f6af8771 --- /dev/null +++ b/设计模式/设计模式之美:Type Object(类型对象).md @@ -0,0 +1,206 @@ +**索引** + +- 意图 + +- 结构 + +- 参与者 + +- 适用性 + +- 效果 + +- 相关模式 + +- 实现 + + - 实现方式(一):Type Object 的经典介绍。 + + - 实现方式(二):Type Object 在游戏设计中的使用。 + +**意图** + +允许在运行时动态灵活的创建新的 "类",而这些类的实例代表着一种不同的对象类型。 + +Allow the flexible creation of new “classes” by creating a single class, each +instance of which represents a different type of object. + +**结构** + +![641315137488.png](media/8e59380941dd441fd6cea190c39f4520.png) + +Type Object +模式包含两个具体类。一个用于描述对象,另一个用于描述类型。每个对象都包含一个指向其类型的指针。 + +![641463105733.png](media/87dce300b403ff117d52bd429afb026d.png) + +**参与者** + +TypeClass + +- 是 TypeObject 的种类。 + +- 每个种类都会有一个单独的类。 + +TypeObject + +- 是 TypeClass 的实例。 + +- 代表着一种对象。定义一种对象所包含的属性和行为。 + +**适用性** + +当以下情况成立时可以使用 Type Object 模式: + +- 类的实例需要根据它们的通用属性或者行为进行分组。 + +- 类需要为每个分组定义一个子类来实现该分组的通用属性和行为。 + +- 类需要大量的子类或者多种变化的子类甚至无法预期子类的变化。 + +- 你需要有能力在运行时创建一些无法在设计阶段预测的新的分组。 + +- 你需要有能力在类已经被实例化的条件下更改一个对象的子类。 + +**效果** + +- 运行时创建新的类型对象。 + +- 避免子类膨胀。 + +- 客户程序无需了解实例与类型的分离。 + +- 可以动态的更改类型。 + +**相关模式** + +- Type Object 模式有些类似于 + [Strategy ](http://www.cnblogs.com/gaochundong/p/design_pattern_strategy.html)和 + [State ](http://www.cnblogs.com/gaochundong/p/design_pattern_state.html)模式。这三种模式都是通过将对象内部的一些行为代理到外部的对象中。Stategy + 和 State 通常是纯行为的代理,而 Type Object 则包含更多个共享数据状态。State + 可以被频繁的更改,Type Object 则很少被改变。Strategy + 通常仅包含一个职责,Type Object 则通常包含多个职责。 + +- Type Object 的实现与 + [Bridge ](http://www.cnblogs.com/gaochundong/p/design_pattern_bridge.html)模式中的 + Abstraction 和 Implementor 的关系很像。区别在于,客户程序可以与 Type Object + 直接协作,而不会直接与 Implementor 进行交互。 + +- Type Object 有点像 + [Flyweight ](http://www.cnblogs.com/gaochundong/p/design_pattern_flyweight.html)一样处理它的对象。两个对象使用相同的 + Type Object 可能看起来是使用的各自的实例,但实际是共享的对象。 + +- Type Object 可以解决多个对象共享数据和行为的问题。类似的问题也可以用 + [Prototype ](http://www.cnblogs.com/gaochundong/p/design_pattern_prototype.html)模式来解决。 + +**实现** + +**实现方式(一):Type Object 的经典介绍。** + +- **TypeClass** - Movie + +- **TypeObject** - Star Wars, The Terminator, Independence Day + +- **Class** - Videotape + +- **Object** - John's Star Wars, Sue's Star Wars + +![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif) + +复制代码 + +1 namespace TypeObjectPattern.Implementation1 2 { 3 public class Movie 4 { 5 +public string Title { get; set; } 6 public float RentalPrice { get; set; } 7 } 8 +9 public class Videotape 10 { 11 public Videotape(Movie movie) 12 { 13 +this.Movie = movie;14 } 15 16 public Movie Movie { get; private set; }17 18 +public Customer Renter { get; private set; }19 public bool IsRented { get; +private set; }20 21 public void RentTo(Customer customer) 22 { 23 IsRented = +true;24 Renter = customer; 25 Renter.ChargeForRental(this.Movie.RentalPrice);26 +} 27 } 28 29 public class Customer 30 { 31 public string Name { get; set; }32 33 +public void ChargeForRental(float rental) 34 { 35 // pay money36 } 37 } 38 39 +public class Client 40 { 41 public void TestCase1() 42 { 43 Customer john = new +Customer() { Name = "John" }; 44 Customer sue = new Customer() { Name = "Sue" }; +45 46 Movie starWars = new Movie() 47 { 48 Title = "Star Wars",49 RentalPrice = +100,50 }; 51 Movie terminator = new Movie() 52 { 53 Title = "The Terminator",54 +RentalPrice = 200,55 }; 56 57 Videotape starWarsVideotapeForJohn = new +Videotape(starWars); 58 starWarsVideotapeForJohn.RentTo(john); 59 60 Videotape +starWarsVideotapeForSue = new Videotape(starWars); 61 +starWarsVideotapeForSue.RentTo(john); 62 63 Videotape terminatorVideotapeForJohn += new Videotape(terminator); 64 terminatorVideotapeForJohn.RentTo(john); 65 } 66 +} 67 } + +![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif) + +复制代码 + +**实现方式(二):Type Object 在游戏设计中的使用。** + +想象我们正在制作在一个虚拟角色扮演游戏。我们的任务是设计一些邪恶的怪兽(Monster)来试图杀掉我们的英雄(Hero)。怪兽有着一些不同的属性,例如生命值(Health)、攻击力(Attacks)、图像、声音等,但以举例为目的我们仅考虑前两个属性。 + +游戏中的每个怪兽都有自己的生命值。生命值从满血开始,每次怪兽被创伤,生命值减少。怪兽会有一个用于描述攻击的字符串,当怪兽攻击英雄时,这个字符串会被显示到用户屏幕上。 + +游戏设计师告诉我们,怪兽会有不同的品种(Breed),例如:猛龙(Dragon)和巨魔(Troll)。每个怪兽品种都描述了一种怪兽,在一个场景下会有多个同一种的怪兽遍布在地牢(Dungeon)中。 + +怪兽的品种(Breed)决定的怪兽的起始生命值,比如猛龙(Dragon)的生命值会比巨魔(Troll)的高,以使猛龙更难被杀掉。同时,同一个品种的怪兽的攻击字符串也是相同的。 + +通过典型的 OO 设计,我们能得到下面这段代码: + +![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif) + +复制代码 + +1 namespace TypeObjectPattern.Implementation2 2 { 3 public abstract class +Monster 4 { 5 public Monster(int startingHealth) 6 { 7 Health = startingHealth; +8 } 9 10 public int Health { get; private set; }11 public abstract string +AttackString { get; }12 } 13 14 public class Dragon : Monster 15 { 16 public +Dragon() 17 : base(500)18 { 19 } 20 21 public override string AttackString 22 { +23 get { return "The dragon breathes fire!"; }24 } 25 } 26 27 public class Troll +: Monster 28 { 29 public Troll() 30 : base(300)31 { 32 } 33 34 public override +string AttackString 35 { 36 get { return "The troll clubs you!"; }37 } 38 } 39 +40 public class Client 41 { 42 public void TestCase2() 43 { 44 Monster dragon = +new Dragon(); 45 Monster troll = new Troll(); 46 } 47 } 48 } + +![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif) + +复制代码 + +这段代码浅显易懂,使用继承的方式设计类的层级结构。一个 Dragon 是一个 +Monster,满足了 "is a" 的关系。每一个怪物的品种都会用一个子类来实现。 + +![442428579944.png](media/2e18e1a71f5bcf86ff211a4ecad48cbc.png) + +如果游戏中有成百上千的怪物种类,则类的继承关系变得庞大。同时也意味着,增加新的怪物品种就需要增加新的子类代码。 + +这是可以工作的,但并不是唯一的选择。我们可以尝试另外一种架构。 + +因为变化较多的部分是品种(Breed)的属性配置,包括生命值和攻击字符串。 + +所以我们可以将品种(Breed)抽取成单独的类,每个怪物类(Monster)包含一个品种类(Breed)。 + +![446492167248.png](media/a5922a22f70dce34455e19954aeac363.png) + +Breed 类用于定义 Monster 的 "type"。每一个 Breed 的实例描述着一种 Monster +对象的概念上的 "type"。 + +![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif) + +复制代码 + +1 namespace TypeObjectPattern.Implementation3 2 { 3 public class Breed 4 { 5 +public int Health { get; set; } 6 public string AttackString { get; set; } 7 } 8 +9 public class Monster 10 { 11 private Breed \_breed; 12 13 public Monster(Breed +breed) 14 { 15 \_breed = breed; 16 } 17 18 public int Health 19 { 20 get { +return \_breed.Health; } 21 } 22 23 public string AttackString 24 { 25 get { +return \_breed.AttackString; } 26 } 27 } 28 29 public class Client 30 { 31 +public void TestCase3() 32 { 33 Breed dragonBreed = new Breed() 34 { 35 Health = +500,36 AttackString = "The dragon breathes fire!",37 }; 38 Breed trollBreed = +new Breed() 39 { 40 Health = 300,41 AttackString = "The troll clubs you!",42 }; +43 44 Monster dragon = new Monster(dragonBreed); 45 Monster breed = new +Monster(trollBreed); 46 } 47 } 48 } + +![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif) + +复制代码 + +Type Object +在这里的优势在于,我们可以定义新的类型的怪物,而不用修改代码。并且可以在运行时动态生成新的对象和修改对象的属性。 diff --git a/设计模式/设计模式之美:Visitor(访问者).md b/设计模式/设计模式之美:Visitor(访问者).md new file mode 100644 index 00000000..8ad15d66 --- /dev/null +++ b/设计模式/设计模式之美:Visitor(访问者).md @@ -0,0 +1,229 @@ +设计模式之美:Visitor(访问者) + +**索引** + +- 意图 + +- 结构 + +- 参与者 + +- 适用性 + +- 效果 + +- 相关模式 + +- 实现 + + - 实现方式(一):Visitor 模式结构样式代码。 + + - 实现方式(二):使用 Visitor 模式解构设计。 + + - 实现方式(三):使用 Acyclic 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. + +**结构** + +![659484684950.png](media/2b90215e2319cee7220018f7e74ac572.png) + +**参与者** + +Visitor + +- 为该对象结构中 ConcreteElement 的每一个类声明一个 Visit + 操作。该操作的名字和特征标识了发送 Visit 请求给该访问者的那个类。 + +ConcreteVisitor + +- 实现每个由 Visitor 声明的操作。 + +Element + +- 定义一个 Accept 操作,它以一个 Visitor 为参数。 + +ConcreteElement + +- 实现 Accept 操作,该操作以一个 Visitor 为参数。 + +ObjectStructure + +- 能枚举 Element。 + +- 可以提供一个高层的接口以允许该 Visitor 访问它的元素。 + +- 可以是一个 Composite 或是一个集合、列表或无序集合。 + +**适用性** + +在以下情况下可以使用 Visitor 模式: + +- 一个对象结构包含很多类操作,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作。 + +- 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作污染这些对象的类。 + +- 定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。 + +**缺点** + +- 增加新的 ConcreteElement 类很困难。添加新的 ConcreteElement 都要在 Visitor + 中添加一个新的抽象操作。 + +- 可能破坏封装。Visitor 假定 ConcreteElement 接口的功能足够强,足以让 Visitor + 进行它的工作。但有时会迫使你提供 ConcreteElement 的内部状态的公共操作。 + +**效果** + +- Visitor 模式使得易于增加新的操作。 + +- Visitor 集中相关的操作而分离无关的操作。 + +**相关模式** + +- Visitor 可以用于对一个 Composite 模式定义的对象结构进行操作。 + +- Visitor 可以用于 Interpreter 解释。 + +**实现** + +**实现方式(一):Visitor 模式结构样式代码。** + +![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif) + +复制代码 + +1 namespace VisitorPattern.Implementation1 2 { 3 public abstract class Element 4 +{ 5 public abstract void Accept(Visitor visitor); 6 } 7 8 public abstract class +Visitor 9 { 10 public abstract void Visit(ConcreteElementA element); 11 public +abstract void Visit(ConcreteElementB element); 12 } 13 14 public class +ObjectStructure 15 { 16 private List\ \_elements = new +List\(); 17 18 public void Attach(Element element) 19 { 20 +\_elements.Add(element); 21 } 22 23 public void Detach(Element element) 24 { 25 +\_elements.Remove(element); 26 } 27 28 public void Accept(Visitor visitor) 29 { +30 foreach (var element in \_elements) 31 { 32 element.Accept(visitor); 33 } 34 +} 35 } 36 37 public class ConcreteElementA : Element 38 { 39 public string Name +{ get; set; } 40 41 public override void Accept(Visitor visitor) 42 { 43 +visitor.Visit(this); 44 } 45 } 46 47 public class ConcreteElementB : Element 48 +{ 49 public string ID { get; set; } 50 51 public override void Accept(Visitor +visitor) 52 { 53 visitor.Visit(this); 54 } 55 } 56 57 public class +ConcreteVisitorA : Visitor 58 { 59 public override void Visit(ConcreteElementA +element) 60 { 61 Console.WriteLine( 62 "ConcreteVisitorA visited +ConcreteElementA : {0}", 63 element.Name); 64 } 65 66 public override void +Visit(ConcreteElementB element) 67 { 68 Console.WriteLine( 69 "ConcreteVisitorA +visited ConcreteElementB : {0}", 70 element.ID); 71 } 72 } 73 74 public class +ConcreteVisitorB : Visitor 75 { 76 public override void Visit(ConcreteElementA +element) 77 { 78 Console.WriteLine( 79 "ConcreteVisitorB visited +ConcreteElementA : {0}", 80 element.Name); 81 } 82 83 public override void +Visit(ConcreteElementB element) 84 { 85 Console.WriteLine( 86 "ConcreteVisitorB +visited ConcreteElementB : {0}", 87 element.ID); 88 } 89 } 90 91 public class +Client 92 { 93 public void TestCase1() 94 { 95 var objectStructure = new +ObjectStructure(); 96 97 objectStructure.Attach(new ConcreteElementA()); 98 +objectStructure.Attach(new ConcreteElementB()); 99 100 +objectStructure.Accept(new ConcreteVisitorA()); 101 objectStructure.Accept(new +ConcreteVisitorB()); 102 } 103 } 104 } + +![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif) + +复制代码 + +**实现方式(二):使用 Visitor 模式解构设计。** + +假设我们有一个 Employee 类,Employee 分为按时薪计算的 Employee 和按月薪计算的 +Employee。 + +![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif) + +复制代码 + +1 public class Employee 2 { 3 public abstract string GetHoursAndPayReport(); 4 } +5 6 public class HourlyEmployee : Employee 7 { 8 public override string +GetHoursAndPayReport() 9 { 10 // generate the line for this hourly employee11 +return "100 Hours and \$1000 in total.";12 } 13 } 14 15 public class +SalariedEmployee : Employee 16 { 17 public override string +GetHoursAndPayReport() 18 { 19 // do nothing20 return string.Empty;21 } 22 } + +![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif) + +复制代码 + +这段代码的问题是,Employee 类及子类耦合了 Salary Report +相关的职责,这侵犯了单一职责原则(Single Responsibility +Principle),因为其导致每次需要更改 Report 相关的职责时,都需要修改 Employee +类。 + +我们是用 Visitor 模式来解决这个问题。 + +![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif) + +复制代码 + +1 namespace VisitorPattern.Implementation2 2 { 3 public abstract class Employee +4 { 5 public abstract string Accept(EmployeeVisitor visitor); 6 } 7 8 public +class HourlyEmployee : Employee 9 { 10 public override string +Accept(EmployeeVisitor visitor) 11 { 12 return visitor.Visit(this);13 } 14 } 15 +16 public class SalariedEmployee : Employee 17 { 18 public override string +Accept(EmployeeVisitor visitor) 19 { 20 return visitor.Visit(this);21 } 22 } 23 +24 public abstract class EmployeeVisitor 25 { 26 public abstract string +Visit(HourlyEmployee employee); 27 public abstract string Visit(SalariedEmployee +employee); 28 } 29 30 public class HoursPayReport : EmployeeVisitor 31 { 32 +public override string Visit(HourlyEmployee employee) 33 { 34 // generate the +line of the report.35 return "100 Hours and \$1000 in total.";36 } 37 38 public +override string Visit(SalariedEmployee employee) 39 { 40 // do nothing41 return +string.Empty;42 } 43 } 44 } + +![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif) + +复制代码 + +**实现方式(三):使用 Acyclic Visitor 模式解构设计。** + +我们注意到 Employee 类依赖于 EmployeeVisitor 基类。而 EmployeeVisitor 类为每个 +Employee 的子类都提供了一个 Visit 方法。 + +因此,这里形成了一个依赖关系的环。这导致 Visitor 在响应变化时变得复杂。 + +Visitor +模式在类继承关系不是经常变化时可以工作的很好,但在子类衍生频繁的情况下会增加复杂度。 + +此时,我们可以应用 Acyclic Visitor 模式,抽象出窄接口,以使 Employee +子类仅依赖于该窄接口。 + +![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif) + +复制代码 + +1 namespace VisitorPattern.Implementation3 2 { 3 public abstract class Employee +4 { 5 public abstract string Accept(EmployeeVisitor visitor); 6 } 7 8 public +class HourlyEmployee : Employee 9 { 10 public override string +Accept(EmployeeVisitor visitor) 11 { 12 try13 { 14 IHourlyEmployeeVisitor +hourlyEmployeeVisitor = (IHourlyEmployeeVisitor)visitor; 15 return +hourlyEmployeeVisitor.Visit(this);16 } 17 catch (InvalidCastException ex) 18 { +19 Console.WriteLine(ex.Message); 20 } 21 22 return string.Empty;23 } 24 } 25 26 +public class SalariedEmployee : Employee 27 { 28 public override string +Accept(EmployeeVisitor visitor) 29 { 30 try31 { 32 ISalariedEmployeeVisitor +salariedEmployeeVisitor = (ISalariedEmployeeVisitor)visitor; 33 return +salariedEmployeeVisitor.Visit(this);34 } 35 catch (InvalidCastException ex) 36 { +37 Console.WriteLine(ex.Message); 38 } 39 40 return string.Empty;41 } 42 } 43 44 +public interface IHourlyEmployeeVisitor 45 { 46 string Visit(HourlyEmployee +employee); 47 } 48 49 public interface ISalariedEmployeeVisitor 50 { 51 string +Visit(SalariedEmployee employee); 52 } 53 54 public abstract class +EmployeeVisitor 55 { 56 } 57 58 public class HoursPayReport : EmployeeVisitor, +IHourlyEmployeeVisitor 59 { 60 public string Visit(HourlyEmployee employee) 61 { +62 // generate the line of the report.63 return "100 Hours and \$1000 in +total.";64 } 65 } 66 67 public class SalariedPayReport : EmployeeVisitor, +ISalariedEmployeeVisitor 68 { 69 public string Visit(SalariedEmployee employee) +70 { 71 return "Something";72 } 73 } 74 } + +![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif) + +复制代码 diff --git a/设计模式/里氏替换原则(Liskov Substitution Principle).md b/设计模式/里氏替换原则(Liskov Substitution Principle).md new file mode 100644 index 00000000..79328ee3 --- /dev/null +++ b/设计模式/里氏替换原则(Liskov Substitution Principle).md @@ -0,0 +1,248 @@ +里氏替换原则(Liskov Substitution Principle) + +开放封闭原则(Open Closed +Principle)是构建可维护性和可重用性代码的基础。它强调设计良好的代码可以不通过修改而扩展,新的功能通过添加新的代码来实现,而不需要更改已有的可工作的代码。抽象(Abstraction)和多态(Polymorphism)是实现这一原则的主要机制,而继承(Inheritance)则是实现抽象和多态的主要方法。 + +那么是什么设计规则在保证对继承的使用呢?优秀的继承层级设计都有哪些特征呢?是什么在诱使我们构建了不符合开放封闭原则的层级结构呢?这些就是本篇文章将要回答的问题。 + +**里氏替换原则(LSP: The Liskov Substitution Principle)** + +使用基类对象指针或引用的函数必须能够在不了解衍生类的条件下使用衍生类的对象。 + +> **Functions that use pointers or references to base classes must be able to +> use objects of derived classes without knowing it.** + +![01060927025.jpeg](media/a963ba27b48428ef5a370d945ce1e679.jpeg) + +Barbara Liskov 在 1988 年提出了这一原则: + +> What is wanted here is something like the following substitution property: +> If for each object o1 of type S there is an object o2 of type T such that +> for all programs P defined in terms of T, the behavior of P is unchanged +> when o1 is substituted for o2 then S is a subtype of T. + +**违背 LSP 原则的一个简单示例** + +一个非常明显地违背 LSP原则的示例就是使用 RTTI(Run Time Type +Identification)来根据对象类型选择函数执行。 + +![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif) + +复制代码 + +1 void DrawShape(const Shape& s) 2 { 3 if (typeid(s) == typeid(Square)) 4 +DrawSquare(static_cast\(s)); 5 else if (typeid(s) == typeid(Circle)) 6 +DrawCircle(static_cast\(s)); 7 } + +![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif) + +复制代码 + +显然 DrawShape 函数的设计存在很多问题。它必须知道所有 Shape +基类的衍生子类,并且当有新的子类被创建时就必须修改这个函数。事实上,很多人看到这个函数的结构都认为是在诅咒面向对象设计。 + +**正方形和长方形,违背原则的微妙之处** + +很多情况下对 LSP 原则的违背方式都十分微妙。设想在一个应用程序中使用了 Rectangle +类,描述如下: + +![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif) + +复制代码 + +1 public class Rectangle 2 { 3 private double \_width; 4 private double +\_height; 5 6 public void SetWidth(double w) { \_width = w; } 7 public void +SetHeight(double w) { \_height = w; } 8 public double GetWidth() { return +\_width; } 9 public double GetHeight() { return \_height; } 10 } + +![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif) + +复制代码 + +试想这个应用程序可以良好地工作,并且已被部署到了多个位置。就像所有成功的软件一样,它的用户提了新的需求。假设某一天用户要求该应用程序除了能够处理长方形(Rectangle)之外还要能够处理正方形(Square)。 + +通常来说,继承关系是 is-a 的关系。换句话讲,如果一种新的对象与一种已有对象满足 +is-a 的关系,那么新的对象的类应该是从已有对象的类继承来的。 + +很明显一个正方形是一个长方形,可以满足所有常规的目的和用途。因此这就建立了 is-a +的关系,Square 的逻辑模型可以从 Rectangle 衍生。 + +![328059832146.png](media/5568d552b41e34f340727f1b1d17df21.png) + +对 is-a 关系的使用是面向对象分析(Object Oriented +Analysis)的基本技术之一。一个正方形是一个(is-a)长方形,所有 Square 类应当从 +Rectangle +类衍生。然而这种思考方式将引起一些微妙的却很严重的问题。通常在我们没有实际使用这些代码之前,这些问题是无法被预见的。 + +关于这个问题,我们的第一个线索可能是Square 类并不需要 \_height 和 \_width +成员变量,尽管无论如何它都继承了它们。可以看出这是一种浪费,而且如果我们持续创建成百上千个 +Square 对象,这种浪费就会表现的十分明显。 + +尽管如此,我们也可以假设我们并不是十分关心内存的开销。那还有什么问题吗?当然!Square +类将继承 SetWidth 和 SetHeight 方法。这些方法对于 Square +来说是完全不适当的,因为一个正方形的长和宽是一样的。这就应该是另一个显著的线索了。然而,有一种方法可以规避这个问题。我们可以覆写SetWidth +和 SetHeight 方法。如下所示: + +![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif) + +复制代码 + +1 public class Square : Rectangle 2 { 3 public void SetWidth(double w) 4 { 5 +base.SetWidth(w); 6 base.SetHeight(w); 7 } 8 public void SetHeight(double w) 9 { +10 base.SetWidth(w);11 base.SetHeight(w);12 } 13 } + +![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif) + +复制代码 + +现在,无论谁设置 Square 对象的 Width,它的 Height 也会相应跟着变化。而当设置 +Height 时,Width 也同样会改变。这样做之后,Square 看起来很完美了。Square +对象仍然是一个看起来很合理的数学中的正方形。 + +1 public void TestCase1() 2 { 3 Square s = new Square(); 4 s.SetWidth(1); // +Fortunately sets the height to 1 too.5 s.SetHeight(2); // sets width and heigt +to 2, good thing.6 } + +但现在看下下面这个方法: + +1 void f(Rectangle r) 2 { 3 r.SetWidth(32); // calls Rectangle::SetWidth4 } + +如果我们传递一个 Square 对象的引用到这个方法中,则 Square 对象将被损坏,因为它的 +Height 将不会被更改。这里明确地违背了 LSP +原则,此函数在衍生对象为参数的条件下无法正常工作。而失败的原因是因为在父类 +Rectangle 中没有将 SetWidth 和 SetHeight 设置为 virtual 函数。 + +我们也能很容易的解决这个问题。但尽管这样,当创建一个衍生类将导致对父类做出修改,通常意味着这个设计是有缺陷的,具体的说就是它违背了 +OCP 原则。我们可能会认为真正的设计瑕疵是忘记了将SetWidth 和 SetHeight 设置为 +virtual 函数,而且我们已经修正了这个问题。但是,其实也很难自圆其说,因为设置 +Rectangle 的 Height 和 Width +已经不再是一个原子操作。无论是何种原因我们将它们设置为 virtual,我们都将无法预期 +Square 的存在。 + +还有,假设我们接收了这个参数,并且解决了这些问题。我们最终得到了下面这段代码: + +![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif) + +复制代码 + +1 public class Rectangle 2 { 3 private double \_width; 4 private double +\_height; 5 6 public virtual void SetWidth(double w) { \_width = w; } 7 public +virtual void SetHeight(double w) { \_height = w; } 8 public double GetWidth() { +return \_width; } 9 public double GetHeight() { return \_height; } 10 } 11 12 +public class Square : Rectangle 13 { 14 public override void SetWidth(double w) +15 { 16 base.SetWidth(w);17 base.SetHeight(w);18 } 19 public override void +SetHeight(double w) 20 { 21 base.SetWidth(w);22 base.SetHeight(w);23 } 24 } + +![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif) + +复制代码 + +**问题的根源** + +此时此刻我们有了两个类,Square 和 Rectangle,而且看起来可以工作。无论你对 Square +做什么,它仍可以保持与数学中的正方形定义一致。而且也不管你对 Rectangle +对象做什么,它也将符合数学中长方形的定义。并且当你传递一个 Square +对象到一个可以接收 Rectangle 指针或引用的函数中时,Square +仍然可以保证正方形的一致性。 + +既然这样,我们可能得出结论了,这个模型现在是自洽的(self-consistent)和正确的。但是,这个结论其实是错误的。一个自洽的模型不一定对它的所有用户都保持一致! + +*(注:自洽性即逻辑自洽性和概念、观点等的前后一贯性。首先是指建构一个科学理论的若干个基本假设之间,基本假设和由这些基本假设逻辑地导出的一系列结论之间,各个结论之间必须是相容的,不相互矛盾的。逻辑自洽性也要求构建理论过程中的所有逻辑推理和数学演算正确无误。逻辑自洽性是一个理论能够成立的必备条件。)* + +试想下面这个方法: + +1 void g(Rectangle r) 2 { 3 r.SetWidth(5);4 r.SetHeight(4);5 +Assert.AreEqual(r.GetWidth() \* r.GetHeight(), 20);6 } + +这个函数调用了 SetWidth 和 SetHeight 方法,并且认为这些函数都是属于同一个 +Rectangle。这个函数对 Rectangle 是可以工作的,但是如果传递一个 Square +参数进去则会发生断言错误。 + +所以这才是真正的问题所在:写这个函数的程序员是否完全可以假设更改一个 Rectangle +的 Width 将不会改变 Height 的值? + +很显然,写这个函数 g 的程序员做了一个非常合理的假设。而传递一个 Square +到这样的函数中才会引发问题。因此,那些已存在的接收 Rectangle +对象指针或引用的函数也同样是不能对 Square 对象正常操作的。这些函数揭示了对 LSP +原则的违背。此外,Square 从 Rectangle 衍生也破坏了这些函数,所以也违背了 OCP +原则。 + +**有效性不是内在的** + +这引出了一个非常重要的结论。从孤立的角度看,一个模型无法自己进行有意义地验证。模型的正确性仅能通过它的使用者来表达。例如,孤立地看 +Square 和 +Rectangle,我们发现它们是自洽的并且是有效的。但当我们从一个对基类做出合理假设的程序员的角度来看待它们时,这个模型就被打破了。 + +因此,当考虑一个特定的设计是否合理时,决不能简单的从孤立的角度来看待它,而**必须从该设计的使用者的合理假设的角度来分析**。 + +**到底哪错了?** + +那么到底发生了什么呢?为什么看起来很合理的 Square 和 +Rectangle模型变坏了呢?难道说一个 Square 是一个 Rectangle 不对吗?is-a +的关系不存在吗? + +不!一个正方形可以是一个长方形,但一个 Square 对象绝对不是一个 Rectangle +对象。为什么呢?因为一个 Square 对象的行为与一个 Rectangle +对象的行为是不一致的。从行为的角度来看,一个 Square 不是一个 Rectangle +!而软件设计真正关注的就是行为(behavior)。 + +LSP 原则使我们了解了 OOD 中 is-a +关系是与行为有关的。不是内在的私有的行为,而是外在的公共的行为,是使用者依赖的行为。例如,上述函数 +g 的作者依赖了一个基本事实,那就是 Rectangle 的 Width 和 Height +彼此之间的变化是无依赖关系的。而这种无依赖的关系就是一种外在的公共的行为,并且其他程序员有可能也会这么想。 + +为了仍然遵守 LSP 原则,并同时符合 OCP +原则,**所有的衍生类必须符合使用者所期待的基类的行为**。 + +**契约式设计(Design by Contract)** + +Bertrand Meyer 在 1988 年阐述了 LSP +原则与契约式设计之间的关系。使用契约式设计,类中的方法需要声明前置条件和后置条件。前置条件为真,则方法才能被执行。而在方法调用完成之前,方法本身将确保后置条件也成立。 + +我们可以看到 Rectangle 的 SetWidth 方法的后置条件是: + +1 Contract.Ensures((_width == w) && (_height == +Contract.OldValue\(_height))); + +为衍生类设置前置条件和后置条件的规则是,Meyer 描述的是: + +> …when redefining a routine [in a derivative], you may only replace its +> precondition by a weaker one, and its postcondition by a stronger one. + +换句话说,当通过基类接口使用对象时,客户类仅知道基类的前置条件和后置条件。因此,衍生类对象不能期待客户类服从强于基类中的前置条件。也就是说,它们必须接受任何基类可以接受的条件。而且,衍生类必须符合基类中所定义的后置条件。也就是说,它们的行为和输出不能违背任何已经与基类建立的限制。基类的客户类绝不能对衍生类的输出产生任何疑惑。 + +显然,后置条件 Square::SetWidth(double w) 要弱于 Rectangle::SetWidth(double +w),因为它不符合基类的中的条件子句 "(_height == +Contract.OldValue\(_height))"。所以,Square::SetWidth(double w) +违背了基类定立的契约。 + +有些编程语言,对前置条件和后置条件有直接的支持。你可以直接定义这些条件,然后在运行时验证系统。如果编程语言不能直接支持条件定义,我们也可以考虑手工定义这些条件。 + +**总结** + +开放封闭原则(Open Closed +Principle)是许多面向对象设计启示思想的核心。符合该原则的应用程序在可维护性、可重用性和鲁棒性等方面会表现的更好。[里氏替换原则(Liskov +Substitution +Principle)](http://www.cnblogs.com/gaochundong/p/liskov_substitution_principle.html)则是实现 +OCP +原则的重要方式。只有当衍生类能够完全替代它们的基类时,使用基类的函数才能够被安全的重用,然后衍生类也可以被放心的修改了。 + +**面向对象设计的原则** + +| 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) | + +**参考资料** + +- *LSP:The Liskov Substitution 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*