设计模式 
下列语言的实现主要是C#语言
学习资料:author::设计模式概述 
资料作者:author::ZShijun/DesignPattern 
设计模式面试题(总结最全面的面试题!!!)-CSDN博客 
 
 
一、设计模式基础 1 面向对象 
特性:封装、继承、多态。
目标:设计出高内聚、低耦合的应用程序,最大程度的实现程序的复用,以应对复杂的需求变化。
设计原则:单一职责原则、依赖倒置原则、开闭原则、接口隔离原则、里氏替换原则、合成复用原则、迪米特原则。
 
2 设计模式 
模式就是对前人积累的经验的抽象和升华。简单地说,就是从不断重复出现的事件中发现和抽象出规律,并解决同一类问题的经验总结,在软件工程领域中的模式可分为三个层次。
层次:
惯用法:最底层,语言相关,如引用计数,智能指针,垃圾收集等。 
设计模式:中层,语言无关,如工厂模式,策略模式等。 
架构模式:最高层,语言无关,用于高层决策,实现架构复用,如C/S架构,B/S架构,微服务架构等。 
 
GoF设计模式根据其目的可分为三种类型:
创建型:主要用于创建对象,主要有工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。 
结构型:主要用于处理类或对象的组合,主要有适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。 
行为型:主要用于描述对类或对象之间的交互及职责分配,主要有策略模式、模板方法模式、观察者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。 
 
 
3 七大设计原则 
单一职责原则:一个类只负责一个功能领域中的相应职责。
依赖倒置原则:
高层模块不应该依赖于低层模块,二者都应该依赖于抽象。 
抽象类不应该依赖于细节类,细节类应当依赖于抽象类。 
换言之,要面向接口(抽象类)编程,而不是面向实现编程。 
 
开闭原则:一个软件实体应当对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。
接口隔离原则:使用多个专门的接口,而不使用单一的总接口。
里氏替换原则:所有基类出现的地方必定能被子类替换,且功能不会发生影响。
合成复用原则:尽量使用对象组合/聚合,而不是继承来达到复用的目的:
组合:在初始化以后,内部的属性值不可被替换。比如我创建了一个圆形画布后,就不可以修改了,这是圆形类和画布类组合在了一起。 
聚合:初始化以后,内部的属性值依然可以被替换。比如我创建了一个圆形画布后,可以修改,将圆形画布换成方形,这叫聚合。现在一般都是聚合。 
 
迪米特原则:也叫最小知识原则,一个软件实体应当尽可能少地与其他实体发生相互作用。类与类之间的耦合度应尽量的低,这样如果类发生变化,影响才会最小。
开闭原则是目标,里氏代换原则是基础,依赖倒置原则是手段。
核心思想就是隔离变化,针对接口编程而不是针对实现编程。
 
 
二、创建型 1 工厂模式 1.1 Factory概念 
工厂顾名思义就是创建产品,本质就是用工厂方法代替new操作创建一种实例化对象的方式。根据不同的实现方式和抽象级别又可分为简单工厂,工厂方法和抽象工厂三种模式。
 
1.2 简单工厂模式 
简单工厂又叫做静态工厂方法模式,但不属于23种GOF设计模式之一。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。
一般情况下使用较好,也有人认为简单工厂模式是一种反模式。
优点:
实现了对责任的分割,隔离了变化,因为它提供了专门的工厂类用于创建对象。 
通过配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。 
 
缺点:
集中了所有实例的创建逻辑,违反了单一职责原则。 
扩展困难,一旦添加新产品就不得不修改工厂逻辑,违反了开闭原则。 
 
 
public  static  class  DbConnectionFactory {          private  static  readonly  string  _connectionString = @"Server=(LocalDB)\MSSQLLocalDB; Integrated Security=true;Initial Catalog=FactoryDb" ;          private  static  readonly  string  _dbType = "Sqlserver" ;     public  static  DbConnection CreateDbConnection ()     {                  if  (_dbType == "Sqlserver" )         {             return  new  SqlConnection(_connectionString);         }         else  if  (_dbType == "MySql" )         {             return  new  MySqlConnection(_connectionString);         }         else          {             return  null ;         }     } } public  class  SqlHelper {          public  int  ExecuteNonQuery (string  sql )     {                  using  (DbConnection conn = DbConnectionFactory.CreateDbConnection())         {             using  (DbCommand cmd = conn.CreateCommand())             {                 conn.Open();                 cmd.CommandText = sql;                 return  cmd.ExecuteNonQuery();             }         }     }          public  object  ExecuteScalar (string  sql )     {                  using  (DbConnection conn = DbConnectionFactory.CreateDbConnection())         {             using  (DbCommand cmd = conn.CreateCommand())             {                 conn.Open();                 cmd.CommandText = sql;                 return  cmd.ExecuteScalar();             }         }     } } class  Program {     static  void  Main (string [] args )     {         SqlHelper sqlHelper = new  SqlHelper();                                    string  selectSql = "select count(*) from [Users];" ;         object  count = sqlHelper.ExecuteScalar(selectSql);         Console.WriteLine($"共有{count} 记录" );     } } 
 
interface  IProduct {     void  ShowInfo () ; } class  ConcreteProductA  : IProduct {     public  void  ShowInfo ()     {         Console.WriteLine("This is Concrete Product A." );     } } class  ConcreteProductB  : IProduct {     public  void  ShowInfo ()     {         Console.WriteLine("This is Concrete Product B." );     } } class  SimpleFactory {     public  IProduct CreateProduct (string  productName )     {         Type type = Type.GetType(productName);         if  (type == null  || !typeof (IProduct).IsAssignableFrom(type))         {             throw  new  ArgumentException("Invalid product name." );         }         return  Activator.CreateInstance(type) as  IProduct;     } } class  Program {     static  void  Main ()     {         SimpleFactory factory = new  SimpleFactory();         IProduct productA = factory.CreateProduct("ConcreteProductA" );         productA.ShowInfo();         IProduct productB = factory.CreateProduct("ConcreteProductB" );         productB.ShowInfo();     } } 
 
1.3 工厂方法模式 
定义一个工厂父类,工厂父类负责定义创建对象的公共接口,而子类则负责生成具体的对象。即将类的实例化延迟到工厂类的子类中完成,即由子类来决定应该实例化哪一个类。
优点:符合设计原则。
缺点:类的个数成倍增加,增加了系统的复杂度。
 
public  class  MySqlConnectionFactory  : DbConnectionFactory {     private  static  readonly  string  _connectionString = @"..." ;     public  override  DbConnection CreateDbConnection ()     {         return  new  MySqlConnection(_connectionString);     } } public  class  OracleSqlConnectionFactory  : DbConnectionFactory {     private  static  readonly  string  _connectionString = @"..." ;     public  override  DbConnection CreateDbConnection ()     {         return  null ;     } } public  class  SqlConnectionFactory  : DbConnectionFactory {     private  static  readonly  string  _connectionString = @"Server=(LocalDB)\MSSQLLocalDB; Integrated Security=true;Initial Catalog=FactoryDb" ;     public  override  DbConnection CreateDbConnection ()     {         return  new  SqlConnection(_connectionString);     } } public  class  SqlHelper {     DbConnectionFactory _connectionFactory;          public  SqlHelper (DbConnectionFactory connectionFactory )     {         this ._connectionFactory = connectionFactory;     }          public  int  ExecuteNonQuery (string  sql )     {         using  (DbConnection conn = _connectionFactory.CreateDbConnection())         {             using  (DbCommand cmd = conn.CreateCommand())             {                 conn.Open();                 cmd.CommandText = sql;                 return  cmd.ExecuteNonQuery();             }         }     }          public  object  ExecuteScalar (string  sql )     {         using  (DbConnection conn = _connectionFactory.CreateDbConnection())         {             using  (DbCommand cmd = conn.CreateCommand())             {                 conn.Open();                 cmd.CommandText = sql;                 return  cmd.ExecuteScalar();             }         }     } } class  Program {     static  void  Main (string [] args )     {         SqlHelper sqlHelper = new  SqlHelper(new  SqlConnectionFactory());                                    string  selectSql = "select count(*) from [Users];" ;         object  count = sqlHelper.ExecuteScalar(selectSql);         Console.WriteLine($"共有{count} 记录" );     } } 
 
1.4 抽象工厂模式 
抽象工厂是工厂方法的升级版,为相关或者相互依赖的对象提供一个统一的接口,而且无需指定他们的具体实现类。
比如我要对在工厂方法中扩展对sql参数的自定义,那么我需要再添加参数工厂类,Mysql参数类,Oracle参数类等,这样会产生非常多的类。
因此工厂方法模式的问题:
抽象工厂的特点:
 
public  class  SqlDbFactory  : DbFactory {     private  static  readonly  string  _connectionString = @"Server=(LocalDB)\MSSQLLocalDB; Integrated Security=true;Initial Catalog=FactoryDb" ;     public  override  DbConnection CreateDbConnection ()     {         return  new  SqlConnection(_connectionString);     }     public  override  DbParameter CreateDbParameter (string  parameterName, object  value  )     {         return  new  SqlParameter(parameterName, value );     } } public  abstract  class  DbFactory {     public  abstract  DbConnection CreateDbConnection () ;     public  abstract  DbParameter CreateDbParameter (string  parameterName, object  value  ) ; } public  class  SqlHelper {     DbFactory _dbFactory;     public  SqlHelper (DbFactory dbFactory )     {         _dbFactory = dbFactory;     }          public  int  ExecuteNonQuery (string  sql,params  DbParameter[] parms )     {         using  (DbConnection conn = _dbFactory.CreateDbConnection())         {             using  (DbCommand cmd = conn.CreateCommand())             {                 conn.Open();                 cmd.CommandText = sql;                 if  (parms != null )                 {                     cmd.Parameters.AddRange(parms);                 }                 return  cmd.ExecuteNonQuery();             }         }     }          public  object  ExecuteScalar (string  sql, params  DbParameter[] parms )     {         using  (DbConnection conn = _dbFactory.CreateDbConnection())         {             using  (DbCommand cmd = conn.CreateCommand())             {                 conn.Open();                 cmd.CommandText = sql;                 if  (parms != null )                 {                     cmd.Parameters.AddRange(parms);                 }                 return  cmd.ExecuteScalar();             }         }     }          public  DbParameter CreateDbParameter (string  parameterName, object  value  )     {         return  _dbFactory.CreateDbParameter(parameterName, value );     } } class  Program {     static  void  Main (string [] args )     {         DbFactory dbFactory = new  SqlDbFactory();         SqlHelper sqlHelper = new  SqlHelper(dbFactory);         int  age = 12 ;         string  selectSql = $"select count(*) from [Users] where Age>@Age;" ;         object  count = sqlHelper.ExecuteScalar(selectSql,                                                new  DbParameter[] { sqlHelper.CreateDbParameter("@Age" , age) });         Console.WriteLine($"共有{count} 记录" );     } } 
 
2 单例模式 2.1 Singleton概念 
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
目的:
优点:
 
import  threadingimport  timeclass  Singleton :    instance = None      def  __init__ (self, name ):         self.name = name     def  __new__ (cls, *args, **kwargs ):         if  cls.instance:             return  cls.instance         cls.instance = object .__new__(cls)         return  cls.instance obj1 = Singleton('Alex' ) print (obj1)obj2 = Singleton('Bob' ) print (obj2)import  threadingclass  Singleton :    _instance_lock = threading.Lock()     instance = None      def  __init__ (self, name ):         self.name = name     def  __new__ (cls, *args, **kwargs ):         if  not  cls.instance:             with  cls._instance_lock:                 if  not  cls.instance:                     cls.instance = super ().__new__(cls)         return  cls.instance obj1 = Singleton('Alex' ) print (obj1)obj2 = Singleton('Bob' ) print (obj2)
 
2.2 静态类实现 
直接设置一个静态类,不是单例模式,但可以满足需求,常用,可用于生产。
注意:这里的++_counter其实存在高并发问题,严格上应该用Interlocked.Increment(ref _counter)的方式。
优点:使用起来方便,简单。
缺点:
静态类不能继承类,实现接口,不能通过接口或者抽象方法(虚方法)实现多态。 
静态类必须在第一次加载时初始化,如果项目中用不到会导致资源浪费。(资源消耗较小的话,可以适当忽略) 
 
 
public  static  class  SingletonSample1 {     private  static  int  _counter = 0 ;     public  static  int  IncreaseCount ()     {         return  ++_counter;     } } public  static  class  SingletonSample1 {     private  static  int  _counter = 0 ;     public  static  int  IncreaseCount ()     {         return  Interlocked.Increment(ref  _counter);     } } class  Program {     static  void  Main (string [] args )     {         int  count1 = singletonSample1.IncreaseCount();         int  count2 = SingletonSample1.IncreaseCount();         Console.WriteLine($"count1={count1} ,count2={count2} " );     }  } 
 
2.3 内部初始化 
在内部进行静态初始化,可用于生产。
优点:解决了静态类不能继承类,实现接口,不能通过接口或者抽象方法(虚方法)实现多态的问题。
缺点:没有解决第一次加载时初始化,资源浪费的问题。
 
public  sealed  class  SingletonSample2 {          private  static  readonly  SingletonSample2 _instance = new  SingletonSample2();     private  int  _counter = 0 ;     private  SingletonSample2 ()  { }          public  static  SingletonSample2 Instance     {         get          {             return  _instance;         }     }     public  int  IncreaseCount ()     {         return  ++_counter;     } } class  Program {     static  void  Main (string [] args )     {         int  count1 = singletonSample2.Instance.IncreaseCount();         int  count2 = SingletonSample2.Instance.IncreaseCount();         Console.WriteLine($"count1={count1} ,count2={count2} " );     }  } 
 
2.4 过渡类 
过渡阶段,不可用于生产。
SingletonSample3,加if判断:
没有预热,这不叫高并发,叫并发高 - 知乎 (zhihu.com) 
SingletonSample4,加锁:
 
public  class  SingletonSample3 {     private  static  SingletonSample3 _instance;     private  int  _counter = 0 ;     private  SingletonSample3 ()  { }     public  static  SingletonSample3 Instance     {         get          {                          if  (_instance == null )             {                 _instance = new  SingletonSample3();             }             return  _instance;         }     }     public  int  IncreaseCount ()     {         return  ++_counter;     } } public  class  SingletonSample4 {     private  static  SingletonSample4 _instance;     private  static  readonly  object  _locker = new  object ();     private  int  _counter = 0 ;     private  SingletonSample4 ()  { }     public  static  SingletonSample4 Instance     {         get          {                          lock  (_locker)             {                 if  (_instance == null )                 {                     _instance = new  SingletonSample4();                 }                 return  _instance;             }         }     }     public  int  IncreaseCount ()     {         return  ++_counter;     } } class  Program {     static  void  Main (string [] args )     {         int  count1 = singletonSample3.Instance.IncreaseCount();         int  count2 = SingletonSample3.Instance.IncreaseCount();         Console.WriteLine($"count1={count1} ,count2={count2} " );         count1 = singletonSample4.Instance.IncreaseCount();         count2 = SingletonSample4.Instance.IncreaseCount();         Console.WriteLine($"count1={count1} ,count2={count2} " );     } } 
 
2.5 双检锁实现 
if套lock,设置双重锁,,可用于生产。
优点:解决了上述实现方式的各种设计缺陷。
缺点:代码有点复杂。
注:C#中依然有些问题。因为_instance = new SingletonSample5();并不是一步完成的。通常情况下,这一步是先开辟空间,再赋值,再将_instance指向这一段空间。而有些编译器会将该步骤优化成先开辟空间,然后就将_instance指向这一段空间,最后赋值。因此会导致这段空间还没被赋值,_instance就已经指向此处了。若此时还有新的请求到达,那么将会直接返回空的_instance,这是存在问题的。解决方法就是在_instance前加上volatile关键字。
 
public  class  SingletonSample5 {          private  static  volatile  SingletonSample5 _instance;     private  static  readonly  object  _locker = new  object ();     private  int  _counter = 0 ;     private  SingletonSample5 ()  { }     public  static  SingletonSample5 Instance     {         get          {                          if  (_instance == null )             {                 lock  (_locker)                 {                     if  (_instance == null )                     {                         _instance = new  SingletonSample5();                     }                 }             }             return  _instance;         }     }     public  int  IncreaseCount ()     {         return  ++_counter;     } } class  Program {     static  void  Main (string [] args )     {         int  count1 = singletonSample5.Instance.IncreaseCount();         int  count2 = SingletonSample5.Instance.IncreaseCount();         Console.WriteLine($"count1={count1} ,count2={count2} " );     }  } 
 
2.6 懒加载模式 
.Net支持的一种优雅版本的实现方式,强烈建议使用该版本 。类似内部初始化方式。
如何使用 C# 中的 Lazy - 知乎 (zhihu.com) 
Lazy:将对象的创建延迟到第一次需要使用时。
优点:代码优雅简洁同时满足需求。
缺点:当系统中有大量单例模式时,会有较多重复代码。
 
public  class  SingletonSample6 {     private  static  readonly  Lazy<SingletonSample6> _instance         = new  Lazy<SingletonSample6>(() => new  SingletonSample6());     private  int  _counter = 0 ;     private  SingletonSample6 ()  { }     public  static  SingletonSample6 Instance     {         get          {             return  _instance.Value;         }     }     public  int  IncreaseCount ()     {         return  ++_counter;     } } class  Program {     static  void  Main (string [] args )     {         int  count1 = singletonSample6.Instance.IncreaseCount();         int  count2 = SingletonSample6.Instance.IncreaseCount();         Console.WriteLine($"count1={count1} ,count2={count2} " );     }  } 
 
2.7 懒加载泛型 
将懒加载需要重复写的代码封装了起来,拥有多个单例类时建议使用此版本。
优点:封装了重复代码。
缺点:违反了依赖倒置原则。
 
public  class  SingletonSampleBase <TSingleton > where  TSingleton : class {          private  static  readonly  Lazy<TSingleton> _instance         = new  Lazy<TSingleton>(() => (TSingleton)Activator.CreateInstance(typeof (TSingleton), true ));          protected  SingletonSampleBase ()  { }     public  static  TSingleton Instance     {         get          {             return  _instance.Value;         }     } } public  class  SingletonSample7  : SingletonSampleBase <SingletonSample7 >{     private  int  _counter = 0 ;     private  SingletonSample7 ()  { }     public  int  IncreaseCount ()     {         return  ++_counter;     } } class  Program {     static  void  Main (string [] args )     {         int  count1 = singletonSample7.Instance.IncreaseCount();         int  count2 = SingletonSample7.Instance.IncreaseCount();         Console.WriteLine($"count1={count1} ,count2={count2} " );     }  } 
 
2.8 单例案例 
数据库单例案例,实际中不要用单例做对数据库的操作。
实际不使用的原因:
线程安全性: 使用单例模式会引入全局共享的对象实例,可能存在多线程并发访问时的安全性问题,需要额外考虑线程安全措施。而每次操作数据库时都创建一个新的数据库连接或者使用连接池,可以更好地控制线程安全性。
 
资源消耗: 单例模式会在应用程序启动时就创建对象实例并一直存在于内存中,占用资源较多。而数据库连接需要进行资源管理,及时释放连接资源可以更好地避免资源耗尽的问题。
 
灵活性:每次操作数据库时可以根据需要灵活地创建新的数据库连接或者使用现有连接,而不受单例对象的限制,更适合在需要时动态创建和释放资源。
 
 
 
public  class  MySqlHelper  : SqlHelperBase <MySqlHelper >{     private  static  readonly  string  _connectionString = @"..." ;     private  MySqlHelper ()  { }     public  override  DbConnection CreateDbConnection ()     {         return  new  MySqlConnection(_connectionString);     }     public  override  DbParameter CreateDbParameter (string  parameterName, object  value  )     {         return  new  MySqlParameter(parameterName, value );     } } public  interface  ISqlHelper {     int  ExecuteNonQuery (string  sql ) ;     object  ExecuteScalar (string  sql ) ;     DbConnection CreateDbConnection () ;     DbParameter CreateDbParameter (string  parameterName, object  value  ) ; } public  class  SqlHelper  : SqlHelperBase <SqlHelper >{     private  static  readonly  string  _connectionString = @"Server=(LocalDB)\MSSQLLocalDB; Integrated Security=true;Initial Catalog=FactoryDb" ;     private  SqlHelper ()  { }     public  override  DbConnection CreateDbConnection ()     {         return  new  SqlConnection(_connectionString);     }     public  override  DbParameter CreateDbParameter (string  parameterName, object  value  )     {         return  new  SqlParameter(parameterName, value );     } } public  abstract  class  SqlHelperBase <TSingleton >: ISqlHelper  where  TSingleton  : class {     private  static  readonly  Lazy<TSingleton> _instance = new  Lazy<TSingleton>(() => (TSingleton)Activator.CreateInstance(typeof (TSingleton), true ));     public  static  TSingleton Instance     {         get          {             return  _instance.Value;         }     }          public  int  ExecuteNonQuery (string  sql )     {         using  (DbConnection conn = CreateDbConnection())         {             using  (DbCommand cmd = conn.CreateCommand())             {                 conn.Open();                 cmd.CommandText = sql;                 return  cmd.ExecuteNonQuery();             }         }     }          public  object  ExecuteScalar (string  sql )     {         using  (DbConnection conn = CreateDbConnection())         {             using  (DbCommand cmd = conn.CreateCommand())             {                 conn.Open();                 cmd.CommandText = sql;                 return  cmd.ExecuteScalar();             }         }     }     public  abstract  DbConnection CreateDbConnection () ;     public  abstract  DbParameter CreateDbParameter (string  parameterName, object  value  ) ; } class  Program {     static  void  Main (string [] args )     {         ISqlHelper sqlHelper = MySqlHelper.Instance;                                    string  selectSql = "select count(*) from [Users];" ;         object  count = sqlHelper.ExecuteScalar(selectSql);         Console.WriteLine($"共有{count} 记录" );     } } 
 
3 建筑者模式 3.1 Builder概念 
定义:建造者模式是将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。创建者模式隐藏了复杂对象的创建过程,它把复杂对象的创建过程加以抽象,通过子类继承或者重载的方式,动态的创建复杂的、具有复合属性的对象。
角色:
建造者(Builder):不同种类的工人,如打地基的,建房梁的,室内装修的等。 
具体的建造者(ConcreteBuilder):每个工种对应的具体的工人。 
指挥者(Director):工程队总指挥,包工头,指挥具体的建造者建房子。 
具体产品(Product):最终建成的房子。 
 
 
3.2 最简单建筑者 
设置好主要对象和对象属性,自己进行组装。
优点:简单,并且配置可灵活搭配。
缺点:面向了实现编程,用户需要知道太多的创建细节。
 
public  abstract  class  Cpu  { public  abstract  string  Type { get ; set ; } }public  class  HighCpu :Cpu  { public  override  string  Type { get ; set ; } = "6核12线程" ; }public  class  LowCpu :Cpu  { ... }public  abstract  class  Mem  { public  abstract  string  Type { get ; set ; } }public  class  HighCpu :Mem  { ... }public  class  LowCpu :Mem  { ... }public  abstract  class  Screen  { public  abstract  string  Type { get ; set ; } }public  class  HighCpu :Screen  { ... }public  class  LowCpu :Screen  { ... }public  class  Phone {     public  Cpu Cpu { get ; set ; }     public  Screen Screen { get ; set ; }     public  Mem Mem { get ; set ; }     public  void  Show ()     {         Console.WriteLine("手机配置:" );         Console.WriteLine($"CPU:{Cpu?.Type} " );         Console.WriteLine($"内存:{Mem?.Type} " );         Console.WriteLine($"屏幕:{Screen?.Type} " );     } } class  Program {     static  void  Main (string [] args )     {         Phone highPhone = new  Phone();         highPhone.Cpu = new  HighCpu();         highPhone.Mem = new  HighMem();         highPhone.Screen = new  HighScreen();         highPhone.Show();         Console.WriteLine();         Phone lowPhone = new  Phone();         lowPhone.Cpu = new  LowCpu();         lowPhone.Mem = new  LowMem();         lowPhone.Screen = new  LowScreen();         lowPhone.Show();     } } 
 
3.3 工厂方法建筑 
定义一个工厂来完成类属性的创建,可以屏幕创造细节,但是类会非常多。
优点:
缺点:
复杂度急剧增大,类爆炸。 
把配件的组装交给手机类(Phone)处理不合理。 
没有屏蔽手机创造细节。 
 
 
public  abstract  class  Cpu  { public  abstract  string  Type { get ; set ; } }public  abstract  class  CpuBuilder  { public  abstract  Cpu BuildCpu () ; }public  class  HighCpu :Cpu  { public  override  string  Type { get ; set ; } = "6核12线程" ; }public  class  HighCpuBuilder : CpuBuilder {     public  override  Cpu BuildCpu ()     {         return  new  HighCpu();     } } public  class  LowCpu : Cpu  { public  override  string  Type { get ; set ; } = "双核4线程" ; }public  class  LowCpuBuilder  : CpuBuilder {     public  override  Cpu BuildCpu ()     {         return  new  LowCpu();     } } ... public  class  Phone {     public  Phone (ScreenBuilder screenBuilder, CpuBuilder cpuBuilder, MemBuilder memBuilder )     {         this .Screen = screenBuilder.BuildScreen();         this .Cpu = cpuBuilder.BuildCpu();         this .Mem = memBuilder.BuildMem();     }     public  Cpu Cpu { get ; set ; }     public  Screen Screen { get ; set ; }     public  Mem Mem { get ; set ; }     public  void  Show ()     {         Console.WriteLine("手机配置:" );         Console.WriteLine($"CPU:{Cpu?.Type} " );         Console.WriteLine($"内存:{Mem?.Type} " );         Console.WriteLine($"屏幕:{Screen?.Type} " );     } } class  Program {     static  void  Main (string [] args )     {         ScreenBuilder highScreenBuilder = new  HighScreenBuilder();         CpuBuilder highCpuBuilder = new  HighCpuBuilder();         MemBuilder highMemBuilder = new  HighMemBuilder();         Phone phone = new  Phone(highScreenBuilder, highCpuBuilder, highMemBuilder);         phone.Show();         Console.WriteLine();         ScreenBuilder lowScreenBuilder = new  LowScreenBuilder();         CpuBuilder lowCpuBuilder = new  LowCpuBuilder();         MemBuilder lowMemBuilder = new  LowMemBuilder();         Phone lowPhone = new  Phone(lowScreenBuilder, lowCpuBuilder, lowMemBuilder);         lowPhone.Show();     }   } 
 
3.4 标准建筑者模式 
利用抽象工厂+简单工厂实现,简单工厂负责生成最终的对象,抽象工厂负责对最终对象的相关联的属性进行组合。
和工厂模式的区别:工厂模式是创建了一组相关联的对象,然后对象之间进行操作。而建筑者模式是在内部对关联对象直接进行操作,最终只生成一个更大的对象。
优点:
一定程度上,消除了类爆炸问题。 
职责分离,由单独一个生产线组装手机。 
 
缺点:
配件配置变得固定了,不能随意组合。 
对大多数场景依然过于复杂,比如,未必每一个配置的手机都需要一个生产线,组装手机也未必需要一个单独的生产线。 
 
 
public  abstract  class  Cpu  { public  abstract  string  Type { get ; set ; } }public  class  LowCpu :Cpu  { public  override  string  Type { get ; set ; } = "双核4线程" ; }public  class  HighCpu :Cpu  { public  override  string  Type { get ; set ; } = "6核12线程" ; }...      public  class  LowPhonePartBuilder : PhonePartBuilder {     public  override  Cpu BuildCpu ()  { return  new  LowCpu(); }     public  override  Mem BuildMem ()  { return  new  LowMem(); }     public  override  Screen BuildScreen ()  { return  new  LowScreen(); } }     public  class  HighPhonePartBuilder : PhonePartBuilder {     public  override  Cpu BuildCpu ()  { return  new  HighCpu(); }     public  override  Mem BuildMem ()  { return  new  HighMem(); }     public  override  Screen BuildScreen ()  { return  new  HighScreen(); } }      public  abstract  class  PhonePartBuilder {     public  abstract  Cpu BuildCpu () ;     public  abstract  Mem BuildMem () ;     public  abstract  Screen BuildScreen () ; }          public  class  PhoneFactory {     private  PhonePartBuilder _phonePartBuilder;     public  PhoneFactory (PhonePartBuilder phonePartBuilder )     {         _phonePartBuilder = phonePartBuilder;     }     public  void  SetPhoneBuilder (PhonePartBuilder phonePartBuilder )     {         _phonePartBuilder = phonePartBuilder;     }     public  Phone BuildPhone ()     {         Phone phone = new  Phone();         phone.Cpu = _phonePartBuilder?.BuildCpu();         phone.Mem = _phonePartBuilder?.BuildMem();         phone.Screen = _phonePartBuilder?.BuildScreen();         return  phone;     } }          class  Program {     static  void  Main (string [] args )     {         PhonePartBuilder highPartBuilder = new  HighPhonePartBuilder();                  PhoneFactory phoneFactory = new  PhoneFactory(highPartBuilder);         Phone highPhone = phoneFactory.BuildPhone();         highPhone.Show();         Console.WriteLine();                  phoneFactory.SetPhoneBuilder(new  LowPhonePartBuilder());         Phone lowPhone = phoneFactory.BuildPhone();         lowPhone.Show();     } } 
 
3.5 优化建筑者模式 
只保留一个最终对象的建筑者,其他的通过接口在内部实现。
优点:简单,灵活,代码优雅。
缺点:用户使用成本相对较高,需要使用者自己配置内部参数。
 
public  class  Cpu  { public  string  Type { get ; set ; } }public  class  Mem  { public  string  Type { get ; set ; } }public  class  Screen  { public  string  Type { get ; set ; } }public  class  Phone {     public  Cpu Cpu { get ; set ; }     public  Screen Screen { get ; set ; }     public  Mem Mem { get ; set ; }          public  void  Show ()     {         Console.WriteLine("手机配置:" );         Console.WriteLine($"CPU:{Cpu?.Type} " );         Console.WriteLine($"内存:{Mem?.Type} " );         Console.WriteLine($"屏幕:{Screen?.Type} " );     } } public  interface  IPhoneBuilder {     IPhoneBuilder BuildCpu (Action<Cpu> buildCpuDelegate ) ;     IPhoneBuilder BuildMem (Action<Mem> buildMemDelegate ) ;     IPhoneBuilder BuildScreen (Action<Screen> buildScreenDelegate ) ;     Phone Build () ; } public  class  PhoneBuilder :IPhoneBuilder {     private  Phone _phone;     private  Cpu _cpu;     private  Mem _mem;     private  Screen _screen; 	     public  IPhoneBuilder BuildCpu (Action<Cpu> buildCpuDelegate )     {         _cpu = new  Cpu();         buildCpuDelegate?.Invoke(_cpu);         return  this ;     }     public  IPhoneBuilder BuildMem (Action<Mem> buildMemDelegate )     {         _mem = new  Mem();         buildMemDelegate?.Invoke(_mem);         return  this ;     }     public  IPhoneBuilder BuildScreen (Action<Screen> buildScreenDelegate )     {         _screen = new  Screen();         buildScreenDelegate?.Invoke(_screen);         return  this ;     }     public  Phone Build ()     {         _phone = new  Phone();         _phone.Cpu = _cpu??new  Cpu() { Type="4核8线程" };         _phone.Mem = _mem??new  Mem() { Type = "8G"  };         _phone.Screen = _screen??new  Screen() { Type = "7寸"  };         return  _phone;     } } class  Program {     static  void  Main (string [] args )     {         IPhoneBuilder phoneBuilder = new  PhoneBuilder();                  Phone phone = phoneBuilder             .BuildCpu(cpu => { cpu.Type = "8核16线程" ; })             .BuildMem(mem => { mem.Type = "32G" ; })             .BuildScreen(screen => { screen.Type = "10寸" ; })             .Build();         phone.Show();     } } 
 
4 原形模式 4.1 Prototype概念 
定义:原型模式是用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。简单地说就是,首先创建一个实例,然后通过这个实例去拷贝(克隆)创建新的实例。
案例:通常情况下,找工作时,需要准备多份简历,简历信息大致相同,但是可以根据不同的公司的岗位需求微调工作经历细节,以及薪资要求。
使用场景:
当需要重复创建一个包含大量公共属性,而只需要修改少量属性的对象时。 
当需要重复创建一个初始化需要消耗大量资源的对象时。 
 
优点:创建大量重复的对象,同时保证性能。
注意:
尽量将实现原型模式的类标记为sealed。 
尽量避免使用ICloneable接口。 
 
 
4.2 手动克隆 
手动将克隆的内容写在Clone中。因为新加内容后需要修改源码,因此违背了开闭原则。
该方法要多次new了ItResume对象(可以通过观察构造函数的执行次数发现),一样需要消耗资源。
 
public  class  BasicInfo {     public  string  Name { get ; set ; }     public  string  Gender { get ; set ; }     public  int  Age { get ; set ; }     public  string  ExpectedSalary { get ; set ; } } public  class  WorkExperence {     public  string  Company { get ; set ; }     public  string  Detail { get ; set ; }     public  DateTime StartDate { get ; set ; }     public  DateTime EndDate { get ; set ; }     public  void  Display ()     {         Console.WriteLine("工作经历:" );         Console.WriteLine($"{this .Company} \t{this .StartDate.ToShortDateString()} -{EndDate.ToShortDateString()} " );         Console.WriteLine("工作详细:" );         Console.WriteLine(this .Detail);     } } public  abstract  class  ResumeBase {     public  string  Name { get ; set ; }     public  string  Gender { get ; set ; }     public  int  Age { get ; set ; }     public  string  ExpectedSalary { get ; set ; }     public  abstract  void  Display () ;          public  abstract  ResumeBase Clone () ; } public  class  ItResume  : ResumeBase {          public  ItResume ()  {}          public  WorkExperence WorkExperence { get ; set ; }     public  override  void  Display ()     {         Console.WriteLine($"姓名:\t{this .Name} " );         Console.WriteLine($"性别:\t{this .Gender} " );         Console.WriteLine($"年龄:\t{this .Age} " );         Console.WriteLine($"期望薪资:\t{this .ExpectedSalary} " );         Console.WriteLine("--------------------------------" );         if  (this .WorkExperence != null )         {             this .WorkExperence.Display();         }         Console.WriteLine("--------------------------------" );     } 	          public  override  ResumeBase Clone ()     {                  ItResume resume = new  ItResume()         {             Name = this .Name,             Gender = this .Gender,             Age = this .Age,             ExpectedSalary = this .ExpectedSalary,             WorkExperence = new  WorkExperence             {                 Company = this .WorkExperence.Company,                 Detail = this .WorkExperence.Detail,                 StartDate = this .WorkExperence.StartDate,                 EndDate = this .WorkExperence.EndDate             }         };         return  resume;     } } public  interface  IResumeBuilder {          IResumeBuilder BuildBasicInfo (Action<BasicInfo> buildBasicInfoDelegate ) ;     IResumeBuilder BuildWorkExperence (Action<WorkExperence> buildWorkExperenceDelegate ) ;     ResumeBase Build () ; } public  class  ResumeBuilder  : IResumeBuilder {     private  readonly  BasicInfo _basicInfo = new  BasicInfo();     private  readonly  WorkExperence _workExperence = new  WorkExperence();     public  IResumeBuilder BuildBasicInfo (Action<BasicInfo> buildBasicInfoDelegate )     {         buildBasicInfoDelegate?.Invoke(_basicInfo);         return  this ;     }     public  IResumeBuilder BuildWorkExperence (Action<WorkExperence> buildWorkExperenceDelegate )     {         buildWorkExperenceDelegate?.Invoke(_workExperence);         return  this ;     }     public  ResumeBase Build ()     {         ItResume resume = new  ItResume()         {             Name = this ._basicInfo.Name,             Gender = this ._basicInfo.Gender,             Age = this ._basicInfo.Age,             ExpectedSalary = this ._basicInfo.ExpectedSalary,             WorkExperence = new  WorkExperence             {                 Company = this ._workExperence.Company,                 Detail = this ._workExperence.Detail,                 StartDate = this ._workExperence.StartDate,                 EndDate = this ._workExperence.EndDate             }         };         return  resume;     } } class  Program {     static  void  Main (string [] args )     {         IResumeBuilder resumeBuilder = new  ResumeBuilder()             .BuildBasicInfo(resume =>                             {                                 resume.Name = "张三" ;                                 resume.Age = 18 ;                                 resume.Gender = "男" ;                                 resume.ExpectedSalary = "100W" ;                             })             .BuildWorkExperence(work =>                                 {                                     work.Company = "A公司" ;                                     work.Detail = "负责XX系统开发,精通YY。。。。。" ;                                     work.StartDate = DateTime.Parse("2019-1-1" );                                     work.EndDate = DateTime.Parse("2020-1-1" );                                 });         ResumeBase resume1 = resumeBuilder             .Build();         ItResume resume2 = resume1.Clone() as  ItResume;         resume2.ExpectedSalary = "面议" ;         resume2.WorkExperence.Detail = "aaaaaaaaaa" ;         resume1.Display();         resume2.Display();     } } 
 
4.3 浅拷贝克隆 
部分内容直接浅拷贝,需要深拷贝的内容将手动进行拷贝。一样违背了开闭原则。
该方法只需要new了一次ItResume对象(可以通过观察构造函数的执行次数发现),其他的通过拷贝完成,相对消耗资源较少。
 
public  class  WorkExperence {     ...     public  WorkExperence Clone ()  { return  this .MemberwiseClone() as  WorkExperence; } } public  override  ResumeBase Clone (){          ItResume itResume = this .MemberwiseClone() as  ItResume;          itResume.WorkExperence = this .WorkExperence.Clone();     return  itResume; } 
 
4.4 序列化克隆 
通过序列化对象,可以将对象转换为字节流,然后再从字节流中反序列化出一个新的对象,实现了对象的深复制,从而实现了克隆的效果。最推荐。
 
[Serializable ] public  class  WorkExperence  {}[Serializable ] public  abstract  class  ResumeBase  {}[Serializable ] public  sealed  class  ItResume  : ResumeBase  {}public  override  ResumeBase Clone (){     using  (MemoryStream stream = new  MemoryStream())     {         BinaryFormatter bf = new  BinaryFormatter();         bf.Serialize(stream, this );         stream.Position = 0 ;         return  bf.Deserialize(stream) as  ResumeBase;     } } 
 
4.5 ICloneable克隆 
利用C#自带的ICloneable进行克隆。
基本思想如下:
由于只有一个Clone方法,因此调用者无法区分到底是深拷贝还是浅拷贝,会给调用者造成极大的困扰; 
如果基类继承了ICloneable接口,并且非Sealed类型,那么它的所有派生类都需要实现Clone方法。否则,用派生类对象调用Clone方法,返回的对象将会是基类Clone方法创建的对象,这就给派生类带来了沉重的负担,因此在非密封类中应该避免实现 ICloneable 接口,但这个不是ICloneable特有的缺陷,任何一种方式实现原型模式都存在该问题,因此建议将原型模式的实现类设置为密封类。 
Clone方法返回值是object,是非类型安全的; 
 
ICloneable被很多人认为是一个糟糕的设计,其他理由如下:
ICloneable除了标识可被克隆之外,无论作为参数还是返回值都没有任何意义; 
.Net Framework在升级支持泛型至今,都没有添加一个与之对应的ICloneable<T>泛型接口; 
很多框架中为了向下兼容,虽然实现了ICloneable接口,但是内部只提供了一个抛出异常的私有实现,例如SqlConnection。 
 
鉴于上述诸多缺点,在实现原型模式时,ICloneable接口能不用就不要用了,自己定义一个更有意义的方法或许会更好。
 
[Serializable ] public  sealed  class  ItResume  : ICloneable {     public  string  Name { get ; set ; }     public  string  Gender { get ; set ; }     public  int  Age { get ; set ; }     public  string  ExpectedSalary { get ; set ; }     public  WorkExperence WorkExperence { get ; set ; }      	     public  void  Display ()     {         Console.WriteLine($"姓名:\t{this .Name} " );         Console.WriteLine($"性别:\t{this .Gender} " );         Console.WriteLine($"年龄:\t{this .Age} " );         Console.WriteLine($"期望薪资:\t{this .ExpectedSalary} " );         Console.WriteLine("--------------------------------" );         if  (this .WorkExperence != null )         {             this .WorkExperence.Display();         }         Console.WriteLine("--------------------------------" );     }      	     public  object  Clone ()     {         using  (MemoryStream stream = new  MemoryStream())         {             BinaryFormatter bf = new  BinaryFormatter();             bf.Serialize(stream, this );             stream.Position = 0 ;             return  bf.Deserialize(stream);         }     } } 
 
 
三、结构型 1 适配器模式 1.1 Adapter概念 
定义:将一个类的接口转换成客户希望的另外一个接口。适配器模式将原本由于接口不兼容而不能一起工作的那些类可以一起工作。类似于读卡器。
优点:
可以让任何两个没有关联的类一起运行。 
提高了类的复用。 
通过引入一个适配器类来重用现有的类,而无须修改原有结构,遵守了开闭原则。 
 
缺点:过多地使用适配器,会让系统非常零乱,不易整体进行把握。
总结:适配器模式属于补偿机制,专门用来在系统后期扩展、修改时使用。因此,适配器模式也不宜过度使用,如果可以的话,我们应该优先通过重构解决。
 
1.2 电脑适配器案例 
角色:
目标接口:Target,我们期望的接口。 
适配器:Adapter,将被适配者转换成我们期望的形式。 
被适配者:Adaptee,原有的接口。 
 
 
public  class  Computer {          private  IUsb _usb;     public  void  SetUsb (IUsb usb )     {         _usb = usb;     }      	     public  void  ConnectUsb ()     {         if  (_usb != null )         {             _usb.Request();         }     } } public  interface  IUsb {     void  Request () ; } public  class  SdReader  : IUsb {          private  Sd _sd;     public  SdReader (Sd sd )     {         _sd = sd;     }          public  void  Request ()     {         _sd.ReadWrite();     } } public  class  Sd  { public  void  ReadWrite ()  { Console.WriteLine("存取数据" ); } }class  Program {     static  void  Main (string [] args )     {                  Computer computer = new  Computer();                  Sd sd = new  Sd();                  computer.SetUsb(new  SdReader(sd));                  computer.ConnectUsb();     } } 
 
2 代理模式 2.1 Proxy概念 
定义:为其他对象提供一种代理以控制对这个对象的访问。例如防火墙、VPN等。
目的:
在不改变原有代码的基础上,对原有类加以控制。 
访问由于某种原因不能直接访问或者直接访问困难的第三方组件或中间件。 
 
和适配器比较:
适配器模式的目的是接口转换,使原本不兼容而不能一起工作的类可以一起工作。 
代理模式的目的是间接访问和访问控制。 
适配器模式面向的是不能一起工作的两个类,而代理模式是面向原本可以一起工作的两个类。 
 
总结:随着系统复杂度的发展,代理模式更偏向于是一种架构模式,在各种框架中以及与各种中间件交互是是非常常见的,而在我们自己的代码中反而很少见了,它更多的体现的是一种思想,而非代码实现。
 
2.2 图片加载案例 
通过代理模式,可以对图片更好地进行操作。当图片展示时,如果图片已经加载过了,就不用再重复加载,提高了效率。这种延迟加载的方式可以优化系统性能,减少资源消耗。
这里体现了和适配器模式的不同之处,适配器模式需要进行适配才能正常使用,而代理模式原件本身是可以直接使用的,只是通过代理对象可以更加方便和安全地对原件进行控制和管理。代理模式提供了对原始对象的间接访问,通过代理对象可以在不改变原始对象的情况下,增加额外的功能或控制访问权限。
 
using  System;public  interface  IImage {     void  Display () ; } public  class  RealImage  : IImage {     private  string  _fileName;     public  RealImage (string  fileName )     {         _fileName = fileName;         LoadImageFromDisk();     }     private  void  LoadImageFromDisk ()     {         Console.WriteLine("Loading image from disk: "  + _fileName);     }     public  void  Display ()     {         Console.WriteLine("Displaying image: "  + _fileName);     } } public  class  ProxyImage  : IImage {     private  RealImage _realImage;     private  string  _fileName;     public  ProxyImage (string  fileName )     {         _fileName = fileName;     }     public  void  Display ()     {         if  (_realImage == null )         {             _realImage = new  RealImage(_fileName);         }         _realImage.Display();     } } class  Program {     static  void  Main ()     {         IImage image = new  ProxyImage("test.jpg" );                  image.Display();                  image.Display();     } } 
 
3 外观模式 3.1 Facade概念 
定义:为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
目的:
为一个复杂的模块或子系统提供一个一致的外界访问接口,降低客户端访问子系统的复杂度。 
使客户端与子系统之间解耦,让子系统内部模块更易维护和扩展。 
进行访问控制,提高系统安全性。 
维护大型遗留系统。 
 
外观模式跟代理模式类似,也更偏向于架构模式,常见于企业应用集成中,企业应用集成包括界面集成,业务流程集成(过程集成),控制集成(应用集成,API集成),数据集成四个层面,都与外观模式有密切关系。
外观模式简单来说就是开发好了多个功能,然后为它们做好界面并进行组装。
和代理模式之间的相同点
都可以在不改变子系统代码的基础上,对子系统加以控制; 
原有系统之间都是可以直接访问的,两个模式都是为了让子系统更加容易使用; 
都不应该在其中添加子系统没有的功能。 
 
和代理模式之间的不同点:
外观模式面向的是多个不同的子系统,而代理模式通常面向的是一个(或者多个相同的)子系统。 
外观模式更多强调是对多个子系统的整合,而代理模式更多强调的是对某个子系统的代理。 
 
极端情况下,假如外观模式中的子系统只有一个,就跟代理模式差不多了,这有点像抽象工厂模式和工厂方法模式之间的关系。
 
3.2 回家案例 
外观模式可以将回家后的多个单体操作(如开空调、开灯、关电视、关窗)聚合成一组操作,例如回家、睡觉、出门。这样可以简化用户的操作流程,提高代码的可读性和可维护性。
 
public  class  AirConditioner {     public  void  TurnOn ()  { Console.WriteLine("开空调..." ); }     public  void  TurnOff ()  { Console.WriteLine("关空调..." ); } } public  class  Light {     public  void  TurnOn ()  { Console.WriteLine("开灯..." ); }     public  void  TurnOff ()  { Console.WriteLine("关灯..." ); } } public  class  Tv {     public  void  TurnOn ()  { Console.WriteLine("打开电视机..." ); }     public  void  TurnOff ()  { Console.WriteLine("关闭电视机..." ); } } public  class  Window {     public  void  Open ()  { Console.WriteLine("开窗..." ); }     public  void  Close ()  { Console.WriteLine("关窗..." ); } } public  class  OneClickFacade {     private  static  readonly  Window _window = new  Window();     private  static  readonly  Tv _tv = new  Tv();     private  static  readonly  Light _light = new  Light();     private  static  readonly  AirConditioner _airConditioner = new  AirConditioner();     public  void  BackHome ()     {         Console.WriteLine("--------回家--------" );         _tv.TurnOn();         _light.TurnOn();         _airConditioner.TurnOn();         _window.Open();     }     public  void  Sleep ()     {         Console.WriteLine("--------睡觉--------" );         _window.Close();         _tv.TurnOff();         _light.TurnOff();         _airConditioner.TurnOn();     }     public  void  LeaveHome ()     {         Console.WriteLine("--------出门--------" );         _window.Close();         _tv.TurnOff();         _light.TurnOff();         _airConditioner.TurnOff();     } } class  Program {     static  void  Main (string [] args )     {         OneClickFacade oneClickFacade = new  OneClickFacade();         oneClickFacade.BackHome();         oneClickFacade.Sleep();         oneClickFacade.LeaveHome();     } } 
 
4 装饰器模式 4.1 Decorator概念 
装饰器模式是一种结构型设计模式,它允许在不改变原有对象结构的情况下,动态地添加额外的功能或责任。该模式通过创建一组装饰类来包装原始类,每个装饰类负责添加一部分功能,通过嵌套调用来实现功能层层叠加。
在装饰器模式中,每个具体装饰器类继承自一个通用的抽象装饰器类,同时持有一个指向被装饰对象的引用。这样,客户端可以透明地使用被装饰对象和装饰对象,它们之间的差异对客户端来说是透明的。装饰器模式可以避免继承的混乱,让类的功能可以动态地组合和追加。
总结来说,装饰器模式提供了一种灵活的方式来扩展对象的功能,同时保持对象的简单性和独立性。
装饰器模式跟代理模式类图基本一样,但是,它们之间却有很大的区别:
装饰器模式关注于在一个对象上动态的添加方法,而代理模式关注于控制对对象的访问。 
装饰器模式通常用聚合的方式,而代理模式通常采用组合的方式。 
装饰器模式通常会套用多层,而代理模式通常只有一层。 但是由于他们的结构十分相似,因此很多时候二者可以做同样的事,比如装饰器模式和代理模式都可用于实现AOP(面向切面编程)。 
 
 
4.2  奶茶非装饰器案例 
案例的需求是基类有饮料和配料,然后饮料分奶茶、咖啡等,配料分冰、布丁等。
非装饰器模式中,饮料和配料是独立的,在饮料中加配料的功能是写在饮料中的。
优点:
可任意搭配组合,并且满足新增饮品的需求。 
新增饮品和配料均只需要增加新的类即可,满足开闭原则。 
 
缺点:
依然需要修改饮料类,这在很多时候是不被允许的,或者说做不到的。 
Add方法不合理,饮料不应该具有添加配料的能力。 
 
 
public  class  Bing : Peiliao {     public  override  string  Name => "冰" ;     public  override  int  Price => 0 ; } public  class  Buding : Peiliao {     public  override  string  Name => "布丁" ;     public  override  int  Price => 2 ; } public  class  Hongdou : Peiliao {     public  override  string  Name => "红豆" ;     public  override  int  Price => 1 ; } public  class  Tang : Peiliao {     public  override  string  Name => "糖" ;     public  override  int  Price => 0 ; } public  class  Zhenzhu : Peiliao {     public  override  string  Name => "珍珠" ;     public  override  int  Price => 3 ; } public  class  Kafeibanlv : Peiliao {     public  override  string  Name => "咖啡伴侣" ;     public  override  int  Price => 1 ; } public  abstract  class  Peiliao {     public  abstract  string  Name { get ; }     public  abstract  int  Price { get ; } } public  class  Naicha : Drink {     public  Naicha ()     {         Name = "奶茶" ;         Price = 8 ;     } } public  class  Kafei : Drink {     public  Kafei ()     {         Name = "咖啡" ;         Price = 12 ;     } } public  abstract  class  Drink {     protected  List<Peiliao> Peiliaos = new  List<Peiliao>();          public  string  Name { get ; set ; }     public  int  Price { get ; set ; }     public  int  Cost     {         get          {             int  cost = this .Price;             foreach  (var  peiliao in  Peiliaos)             {                 cost += peiliao.Price;             }             return  cost;         }     }     public  string  Desc     {         get          {             string  desc = this .Name;             foreach  (var  peiliao in  Peiliaos)             {                 desc += "+"  + peiliao.Name;             }             return  desc;         }     }     public  void  AddPeiliao (Peiliao peiliao )  { Peiliaos.Add(peiliao); } } class  Program {     static  void  Main (string [] args )     {         Drink drink = new  Naicha();         drink.AddPeiliao(new  Bing());         drink.AddPeiliao(new  Hongdou());         drink.AddPeiliao(new  Hongdou());         drink.AddPeiliao(new  Zhenzhu());         Console.WriteLine($"描述:{drink.Desc} ,价格:{drink.Cost} " );         Drink drink2 = new  Kafei();         drink2.AddPeiliao(new  Kafeibanlv());         Console.WriteLine($"描述:{drink2.Desc} ,价格:{drink2.Cost} " );     } } 
 
4.3 奶茶装饰器案例 
通过组合+继承的方式改进,可使得饮品的扩展更灵活,同时也遵守了开闭原则。其中,组合是为了实现功能,而继承是为了约束类型,这其实就是装饰者模式。
在下面的代码中,配料不再在饮料中添加,而是在配料中添加功能,通过传递饮料来确定哪个饮料需要加配料(配料组合饮料的同时继承饮料)。
优点:
可动态的给一个对象增加额外的职责。 
有很好地可扩展性。 
 
缺点:增加了程序的复杂度,刚接触理解起来会比较困难。
 
public  class  Bing : Peiliao {     public  Bing (Drink drink ) : base (drink )     {         Name = "冰" ;         Price = 0 ;     } } public  class  Buding : Peiliao {     public  Buding (Drink drink ) : base (drink )     {         Name = "布丁" ;         Price = 2 ;     } } public  class  Hongdou : Peiliao {     public  Hongdou (Drink drink ) : base (drink )     {         Name = "红豆" ;         Price = 1 ;     } } public  class  Tang : Peiliao {     public  Tang (Drink drink ) : base (drink )     {         Name = "糖" ;         Price = 0 ;     } } public  class  Zhenzhu : Peiliao {     public  Zhenzhu (Drink drink ) : base (drink )     {         Name = "珍珠" ;         Price = 3 ;     } } public  class  Kafeibanlv : Peiliao {     public  Kafeibanlv (Drink drink ) : base (drink )     {         Name = "咖啡伴侣" ;         Price = 1 ;     } } public  class  Peiliao : Drink {     protected  readonly  Drink Drink;     public  Peiliao (Drink drink )  { Drink = drink; }     public  override  string  Desc { get  { return  Drink.Desc + "+"  + this .Name; } }     public  override  int  Cost { get  { return  Drink.Cost + this .Price; } } } public  class  Naicha : Drink {     public  Naicha ()     {         Name = "奶茶" ;         Price = 8 ;     }     public  override  string  Desc => this .Name;     public  override  int  Cost => this .Price; } public  class  Kafei : Drink {     public  Kafei ()     {         Name = "咖啡" ;         Price = 12 ;     }     public  override  string  Desc => this .Name;     public  override  int  Cost => this .Price; } public  abstract  class  Drink {     public  string  Name { get ; set ; }     public  int  Price { get ; set ; }     public  abstract  string  Desc { get ; }     public  abstract  int  Cost { get ; } } class  Program {     static  void  Main (string [] args )     {         Drink drink = new  Naicha();         drink = new  Hongdou(drink);         drink = new  Bing(drink);         drink = new  Buding(drink);         Console.WriteLine($"描述:{drink.Desc} ,价格:{drink.Cost} " );         Drink drink2 = new  Kafei();         drink2 = new  Kafeibanlv(drink2);         drink2 = new  Tang(drink2);         Console.WriteLine($"描述:{drink2.Desc} ,价格:{drink2.Cost} " );     } } 
 
5 桥接模式 5.1 Bridge概念 
桥接模式是将抽象部分与它的实现部分分离,使它们都可以独立地变化。
在桥接模式中,抽象部分定义了对象的行为和属性,而实现部分则负责实现这些行为和属性。通过将抽象部分和实现部分独立出来,我们可以在不影响彼此的情况下,扩展或修改它们。桥接模式通过组合关系而非继承关系,避免了类的爆炸性增长,并更灵活地适应不同需求的变化。
跟装饰器模式的区别:
装饰器模式是为了动态地给一个对象增加功能,而桥接模式是为了让类在多个维度上自由扩展。 
装饰器模式的装饰者和被装饰者需要继承自同一父类,而桥接模式通常不需要。 
装饰器模式通常可以嵌套使用,而桥接模式不能。 
 
 
5.2 奶茶桥接案例 
在奶茶点单场景中,将品牌、容量和饮品分离开来,通过组合关系来实现不同品牌、不同容量的饮品组合。
饮品是作用的主体,品牌、容量等是附加属性,组合在饮品当中。
 
public  class  CoCo : BrandBase {     public  override  string  BrandName => "[CoCo]" ;     public  override  int  Price => 2 ; } public  class  Ruixin : BrandBase {     public  override  string  BrandName => "[瑞辛]" ;     public  override  int  Price => 2 ; } public  class  Xicha : BrandBase {     public  override  string  BrandName => "[喜茶]" ;     public  override  int  Price => 3 ; } public  class  Yidiandian : BrandBase {     public  override  string  BrandName => "[一点点]" ;     public  override  int  Price => 2 ; } public  abstract  class  BrandBase {     public  abstract  string  BrandName { get ; }     public  abstract  int  Price { get ; } } public  class  Dabei  : SkuBase {     public  override  string  SkuType => "大杯" ;     public  override  int  Price => 3 ; } public  class  Zhongbei  : SkuBase {     public  override  string  SkuType => "中杯" ;     public  override  int  Price => 2 ; } public  class  Xiaobei  : SkuBase {     public  override  string  SkuType => "小杯" ;     public  override  int  Price => 1 ; } public  abstract  class  SkuBase {     public  abstract  string  SkuType { get ; }     public  abstract  int  Price { get ; } } public  class  Naicha : Drink {     public  Naicha (BrandBase brand, SkuBase sku ):base (brand,sku )     {         Name = "奶茶" ;         Price = 8 ;     } } public  class  Kafei : Drink {     public  Kafei (BrandBase brand, SkuBase sku ) : base (brand, sku )     {         Name = "咖啡" ;         Price = 12 ;     } } public  abstract  class  Drink {     private  readonly  BrandBase _brand;     private  readonly  SkuBase _sku;          public  Drink (BrandBase brand, SkuBase sku )     {         this ._brand = brand;         this ._sku = sku;     }     public  string  Name { get ; set ; }     public  int  Price { get ; set ; }     public  string  Desc     { get  { return  this .Name + this ._brand.BrandName + this ._sku.SkuType; } }     public  int  Cost     { get  { return  this .Price + this ._brand.Price + this ._sku.Price; } } } class  Program {     static  void  Main (string [] args )     {         BrandBase brand = new  Ruixin();         SkuBase sku = new  Dabei();         Drink drink = new  Kafei(brand, sku);         Console.WriteLine($"描述:{drink.Desc} ,价格:{drink.Cost} " );     } } 
 
6 组合模式 6.1 Composite概念 
定义:将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得对单个对象和组合对象的使用具有一致性。
案例:文件系统中的文件与文件夹、Winform中的简单控件与容器控件、XML中的Node和Element等。
优点:
客户端调用简单,可以像处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。 
可以方便的在结构中增加或者移除对象。 
 
缺点:客户端需要花更多时间理清类之间的层次关系。
 
6.2 透明模式 
透明模式是把组合使用的方法放到抽象类中,使得叶子对象和枝干对象具有相同的结构,客户端调用时具备完全一致的行为接口。但因为Leaf类本身不具备Add()、Remove()方法的功能,所以实现它是没有意义的,违背了单一职责原则和里氏替换原则。
 
public  class  CatalogService {     #region  数据      private  static  readonly  List<Node> _nodes = new  List<Node>     {         new  Node{Id=1 ,Name="音乐" ,ParentId=0 },         new  Node{Id=2 ,Name="知识" ,ParentId=0 },         new  Node{Id=3 ,Name="生活" ,ParentId=0 },         new  Node{Id=4 ,Name="科学科普" ,ParentId=2 },         new  Node{Id=5 ,Name="社科人文" ,ParentId=2 },         new  Node{Id=6 ,Name="职业职场" ,ParentId=2 },         new  Node{Id=7 ,Name="野生技术协会" ,ParentId=2 },         new  Node{Id=8 ,Name="搞笑" ,ParentId=3 },         new  Node{Id=9 ,Name="日常" ,ParentId=3 },         new  Node{Id=10 ,Name="摄影" ,ParentId=7 },         new  Node{Id=11 ,Name="编程" ,ParentId=7 },         new  Node{Id=12 ,Name="英语" ,ParentId=7 },         new  Node{Id=13 ,Name="科学科普文章1" ,ParentId=4 },         new  Node{Id=14 ,Name="科学科普文章2" ,ParentId=4 },         new  Node{Id=15 ,Name="摄影文章1" ,ParentId=10 },         new  Node{Id=16 ,Name="编程文章1" ,ParentId=11 },         new  Node{Id=17 ,Name="编程文章2" ,ParentId=11 }     };     #endregion   } public  class  Node {     public  int  Id { get ; set ; }     public  string  Name { get ; set ; }     public  int  ParentId { get ; set ; } } 
 
public  class  Leaf  : Component {     public  Leaf (string  name ) : base (name )  {} 	     public  override  void  Add (Component component )  { throw  new  InvalidOperationException("叶子节点不能添加元素" ); } 	     public  override  void  Remove (Component component )  { throw  new  InvalidOperationException("叶子节点不能删除元素" ); }     public  override  int  SumArticleCount ()  { return  1 ; }     public  override  void  Display (int  depth ) { Console.WriteLine(new  string ('-' , depth) + Name); } } public  class  Composite  : Component {     private  List<Component> _components = new  List<Component>();     public  Composite (string  name ):base (name )  {}          public  override  void  Add (Component component )  { _components.Add(component); }     public  override  void  Remove (Component component )  { _components.Remove(component); }      	     public  override  void  Display (int  depth )     {         Console.WriteLine(new  string ('-' , depth) + Name);         foreach  (Component component in  _components)         {             component.Display(depth + 1 );         }     } 	     public  override  int  SumArticleCount ()     {         int  count = 0 ;         foreach  (var  item in  _components)         {             count += item.SumArticleCount();         }         return  count;     } } public  abstract  class  Component {     public  string  Name { get ; set ; }     public  Component (string  name )  { this .Name = name; }     public  abstract  int  SumArticleCount () ;     public  abstract  void  Add (Component component ) ;     public  abstract  void  Remove (Component component ) ;     public  abstract  void  Display (int  depth ) ; } class  Program {     static  void  Main (string [] args )     {         Component root = new  Composite("目录" );         Component music = new  Composite("音乐" );         Component knowledge = new  Composite("知识" );         Component life = new  Composite("生活" );         root.Add(music);         root.Add(knowledge);         root.Add(life);         Component science = new  Composite("科学科普" );         Component tech = new  Composite("野生技术协会" );         knowledge.Add(science);         knowledge.Add(tech);         Component scienceArticle1 = new  Leaf("科学科普文章1" );         Component scienceArticle2 = new  Leaf("科学科普文章2" );         science.Add(scienceArticle1);         science.Add(scienceArticle2);         Component shoot = new  Composite("摄影" );         Component program = new  Composite("编程" );         Component english = new  Composite("英语" );         tech.Add(shoot);         tech.Add(program);         tech.Add(english);         Component shootArticle1 = new  Leaf("摄影文章1" );         Component lifeArticle1 = new  Leaf("生活文章1" );         Component lifeArticle2 = new  Leaf("生活文章2" );         shoot.Add(shootArticle1);         life.Add(lifeArticle1);         life.Add(lifeArticle2);         tech.Remove(program);         knowledge.Display(0 );         Console.WriteLine("文章数:" + knowledge.SumArticleCount());     } } 
 
6.3 安全模式 
安全模式是把枝干和叶子节点区分开来,枝干单独拥有用来组合的方法,这种方法比较安全。但枝干和叶子节点不具有相同的接口,客户端的调用需要做相应的判断,违背了依赖倒置原则。
 
public  class  Leaf  : Component {     public  Leaf (string  name ) : base (name )  {}     public  override  int  SumArticleCount () { return  1 ; }     public  override  void  Display (int  depth )  { Console.WriteLine(new  string ('-' , depth) + Name); } } public  class  Composite  : Component {     private  List<Component> _components = new  List<Component>();     public  Composite (string  name ):base (name )  {}          public  void  Add (Component component )  {  _components.Add(component);  }     public  void  Remove (Component component )  {  _components.Remove(component); }     public  override  void  Display (int  depth )     {         Console.WriteLine(new  string ('-' , depth) + Name);         foreach  (Component component in  _components)         {             component.Display(depth + 1 );         }     }     public  override  int  SumArticleCount ()     {         int  count = 0 ;         foreach  (var  item in  _components)         {             count += item.SumArticleCount();         }         return  count;     } } public  abstract  class  Component {     public  string  Name { get ; set ; }     public  Component (string  name )  { this .Name = name; }     public  abstract  int  SumArticleCount () ;     public  abstract  void  Display (int  depth ) ; } class  Program {     static  void  Main (string [] args )     {                  Composite root = new  Composite("目录" );         Composite music = new  Composite("音乐" );         Composite knowledge = new  Composite("知识" );         Composite life = new  Composite("生活" );         root.Add(music);         root.Add(knowledge);         root.Add(life);         Composite science = new  Composite("科学科普" );         Composite tech = new  Composite("野生技术协会" );         knowledge.Add(science);         knowledge.Add(tech);         Component scienceArticle1 = new  Leaf("科学科普文章1" );         Component scienceArticle2 = new  Leaf("科学科普文章2" );         science.Add(scienceArticle1);         science.Add(scienceArticle2);         Composite shoot = new  Composite("摄影" );         Composite program = new  Composite("编程" );         Composite english = new  Composite("英语" );         tech.Add(shoot);         tech.Add(program);         tech.Add(english);         Component shootArticle1 = new  Leaf("摄影文章1" );         Component lifeArticle1 = new  Leaf("生活文章1" );         Component lifeArticle2 = new  Leaf("生活文章2" );         shoot.Add(shootArticle1);         life.Add(lifeArticle1);         life.Add(lifeArticle2);         root.Display(0 );         Console.WriteLine("文章数:"  + root.SumArticleCount());     } } 
 
7 享元模式 7.1 Flyweight概念 
定义:运用共享技术有效地支持大量细粒度的对象。简单来说就是类的复用,类似于单例模式,但是享元模式是很多个类的实例。
在享元模式中,其实存在共享出来的类和非共享出来的类。但是非共享出来的类用得比较少,一般如果类是非共享状态,那么也就没有创建它的必要了。
适用场景:
系统会用到大量相同或相似的对象。 
对象创建比较耗时。 
 
目的:
案例:活字印刷、图书馆借书、共享单车。
应用实例:字符串驻留池、线程池、数据库连接池等。
优点:
缺点:增加了系统的复杂度。
与单例模式的区别:
享元模式是共享大量类的大量实例,而单例是一个类一个实例。 
单例模式针对的是对象的创建,而享元模式针对的是对象的管理。 
单例模式不能单独创建,而享元模式可以单独创建。 
 
与简单工厂模式的区别:
享元模式在简单工厂模式的基础上加入了缓存。 
简单工厂模式的作用仅仅是创建对象,而享元模式虽然也创建对象,但其主要作用是管理对象。 
 
 
7.2 内部创建型 
利用单例模式 + 简单工厂模式实现对多个类的全局共享。
内部创建方式:工厂中存在已经写好了的要使用的类,使用时即可使用即可。
工厂中用一个字典存某个类是否创建过,创建过就不再重复创建。
 
public  class  ETypeface  : Typeface {     public  override  string  Print ()  { return  "E" ; } } public  class  HTypeface  : Typeface {     public  override  string  Print ()  { return  "H" ; } } public  class  LTypeface  : Typeface {     public  override  string  Print ()  { return  "L" ; } } public  class  OTypeface  : Typeface {     public  override  string  Print ()  { return  "O" ; } } public  abstract  class  Typeface {     public  abstract  string  Print () ; } public  class  TypefaceFactory {     private  static  readonly  IDictionary<Type, Typeface> _typefaces          = new  Dictionary<Type, Typeface>();     private  static  readonly  object  _locker = new  object ();     public  static  Typeface GetTypeface <TTypeface >() where  TTypeface : Typeface     {                  Type type = typeof (TTypeface);         if  (!_typefaces.ContainsKey(type))         {             lock  (_locker)             {                 if  (!_typefaces.ContainsKey(type))                 {                     Typeface typeface = Activator.CreateInstance(typeof (TTypeface)) as  Typeface;                     _typefaces.Add(type, typeface);                 }             }         }         return  _typefaces[type];     } } class  Program {     static  void  Main (string [] args )     {         Console.WriteLine("Hello World!" );         Console.WriteLine($"{TypefaceFactory.GetTypeface<HTypeface>().Print()} "  +                           $"{TypefaceFactory.GetTypeface<ETypeface>().Print()} {TypefaceFactory.GetTypeface<LTypeface>().Print()} "  +                           $"{TypefaceFactory.GetTypeface<LTypeface>().Print()} {TypefaceFactory.GetTypeface<OTypeface>().Print()} " );     } } 
 
7.3 外部创建型 
利用单例模式 + 简单工厂模式实现对多个类的全局共享。
外部创建方式:工厂中只有一个简单的基类,使用时客户端自行向工厂中加入案例并使用。
工厂中用一个字典存某个类是否创建过,创建过就不再重复创建。
 
public  class  WordTypeface  : Typeface {     private  readonly  string  _word;     public  WordTypeface (string  word )  {this ._word = word; }     public  override  string  Print ()  { return  this ._word; } } public  abstract  class  Typeface {     public  abstract  string  Print () ; } public  class  TypefaceFactory {     private  static  readonly  IDictionary<string , Typeface> _typefaces         = new  Dictionary<string , Typeface>();     private  static  readonly  object  _locker = new  object (); 	     public  static  void  SetTypeface (string  key, Typeface typeface )     {         if  (!_typefaces.ContainsKey(key))          {             lock  (_locker)             {                 if  (!_typefaces.ContainsKey(key))                 {                     _typefaces.Add(key, typeface);                 }             }         }     } 	     public  static  Typeface GetTypeface (string  key )     {         if  (_typefaces.ContainsKey(key))         {             return  _typefaces[key];         }         return  null ;     } } class  Program {     static  void  Main (string [] args )     {         Console.WriteLine("Hello World!" );                  TypefaceFactory.SetTypeface("hello" , new  WordTypeface("Hello" ));         Console.WriteLine(TypefaceFactory.GetTypeface("hello" ).Print());     } } 
 
 
四、行为型 1 模版方法模式 1.1 TemplateMethod概念 
定义:定义一个操作中的算法的框架,而将一些步骤延迟到了子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些步骤。
简单来说就是定义好一个模板,后续用户在使用时可以按照这个模板进行具体实现。
优点:
封装了算法骨架,提高了代码复用性,简化了使用难度。 
封装不变部分,扩展可变部分,满足开闭原则。 
 
缺点:
 
1.2 大象放冰箱案例 
案例:把大象放入冰箱的步骤?
实现:众所周知,把大象放入冰箱需要开冰箱门,放入大象,关冰箱门三步。所以可以定义一个模板,写好这三步。然后在子类中具体实现是放入大象还是放入狗。
 
public  class  ElephantToFridge :AnimalToFridge {     public  override  void  PutIntoFridge ()     {         Console.WriteLine("把大象放进去" );     } } public  class  DogToFridge : AnimalToFridge {     public  override  void  PutIntoFridge ()     {         Console.WriteLine("把狗放进去" );     } } public  abstract  class  AnimalToFridge {     public  void  Do ()     {         OpenFridge();         PutIntoFridge();         CloseFridge();     }     private  void  OpenFridge ()     {         Console.WriteLine("把冰箱门打开" );     }     public  abstract  void  PutIntoFridge () ;     private  void  CloseFridge ()     {         Console.WriteLine("把冰箱门关上" );     } } class  Program {     static  void  Main (string [] args )     {         AnimalToFridge dogToFridge = new  DogToFridge();         dogToFridge.Do();     } } 
 
1.3 长颈鹿放冰箱案例 
案例:把长颈鹿放入冰箱的步骤?
实现:因为大象在冰箱里,所以把长颈鹿放入冰箱需要四步,中间需要加上把大象拿出来这一步。那么在模板中,因为有的方法需要在子类实现有的不需要,所以可以写好一个钩子函数,然后子类进行选择性实现。
钩子函数:钩子就是给子类一个授权,让子类来可重定义模板方法的某些步骤,说白了就是虚函数。
 
public  class  ElephantToFridge  : AnimalToFridge {     protected  override  void  PutIntoFridge ()     {         Console.WriteLine("把大象放进去" );     } } public  class  GiraffeToFridge  : AnimalToFridge {     protected  override  void  BeforePutIntoFridge ()     {         Console.WriteLine("把大象弄出来" );     }     protected  override  void  PutIntoFridge ()     {         Console.WriteLine("把长颈鹿放进去" );     } } public  abstract  class  AnimalToFridge {     public  void  Do ()     {         OpenFridge();                  BeforePutIntoFridge();         PutIntoFridge();         CloseFridge();     }     private  void  OpenFridge ()     {         Console.WriteLine("把冰箱门打开" );     } 	     protected  virtual  void  BeforePutIntoFridge ()  { }     protected  abstract  void  PutIntoFridge () ;     private  void  CloseFridge ()     {         Console.WriteLine("把冰箱门关上" );     } } class  Program {     static  void  Main (string [] args )     {         AnimalToFridge elephant = new  ElephantToFridge();         elephant.Do();         Console.WriteLine("--------------" );         AnimalToFridge giraffe = new  GiraffeToFridge();         giraffe.Do();     } } 
 
1.4 客户端自定义 
利用委托,不用子类进行实现,而是在客户端中进行实现,适合算法不难,不重复使用的场景。
优点:自由度高。
缺点:调用困难,需要客户实现。
 
public  class  AnimalToFridge {     public  void  Do (Action beforePutIntoFridge,Action putIntoFridge )     {         OpenFridge(); 		         beforePutIntoFridge?.Invoke();         putIntoFridge?.Invoke();         CloseFridge();     }     private  void  OpenFridge ()     {         Console.WriteLine("把冰箱门打开" );     }     private  void  CloseFridge ()     {         Console.WriteLine("把冰箱门关上" );     } } class  Program {     static  void  Main (string [] args )     {         AnimalToFridge elephant = new  AnimalToFridge();         elephant.Do(null , () =>                     {                         Console.WriteLine("把大象放进去" );                     });         Console.WriteLine("--------------" );         AnimalToFridge giraffe = new  AnimalToFridge();         giraffe.Do(() =>                    {                        Console.WriteLine("把大象弄出来" );                    }, () =>                    {                        Console.WriteLine("把长颈鹿放进去" );                    });     } } 
 
2 策略模式 2.1 Strategy概念 
定义:定义一系列算法,把他们一个个封装起来,并且使他们可以互相替换。该模式使得算法可以独立于使用它的客户程序而变化。
应用场景:促销活动,日志、缓存等。
 
2.2 画板案例 
策略模式对象:
Context :策略上下文,持有IStrategy的引用,负责和具体的策略实现交互。 
IStrategy :策略接口,约束一系列具体的策略算法。 
ConcreteStrategy :具体的策略实现。 
 
在下述案例中,Context就是画板,IStrategy就是绘画、填充等动作,ConcreteStrategy就是具体的工具,比如用笔画、用刷子画、用刷子填充、用油漆桶填充等。
优点:
策略可以互相替换。 
解决switch-case、if-else带来的难以维护的问题。 
策略易于扩展,满足开闭原则。 
 
缺点:
随着策略的扩展,策略类数量会增多。 
客户端必须知道每一个策略类,增加了使用难度。 
 
 
public  class  PenStrokeStrategy  : IStrokeStrategy {     public  void  Stroke ()     {         Console.WriteLine($"用钢笔描边图形" );     } } public  class  BrushStrokeStrategy  : IStrokeStrategy {     public  void  Stroke ()     {         Console.WriteLine($"用笔刷描边图形" );     } } public  class  BrushFillStrategy  : IFillStrategy {     public  void  Fill ()     {         Console.WriteLine($"用笔刷填充图形" );     } } public  class  BucketFillStrategy  : IFillStrategy {     public  void  Fill ()     {         Console.WriteLine("用油漆桶填充图形" );     } } public  interface  IStrokeStrategy {     void  Stroke () ; } public  interface  IFillStrategy {     void  Fill () ; } public  class  Graphics {     private  IStrokeStrategy _strokeStrategy;     private  IFillStrategy _fillStrategy;     public  Graphics (IStrokeStrategy strokeStrategy,                      IFillStrategy fillStrategy )    {         this ._strokeStrategy = strokeStrategy;         this ._fillStrategy = fillStrategy;     }     public  void  Stroke ()     {         this ._strokeStrategy.Stroke();     }     public  void  Fill ()     {         this ._fillStrategy.Fill();     } } class  Program {     static  void  Main (string [] args )     {         IStrokeStrategy strokeStrategy = new  PenStrokeStrategy();         IFillStrategy fillStrategy = new  BrushFillStrategy();         Graphics graphics = new  Graphics(strokeStrategy, fillStrategy);         graphics.Stroke();         graphics.Fill();     } } 
 
2.3 简单工厂优化 
由上述案例可知,随着策略的扩展,策略类数量会增多。因此我们可以用工厂模式进行管理,下列是用简单工厂进行管理的案例。
 
...; public  enum  StrokeWith{     Pen,     Brush } public  enum  FillWith{     Brush,     Bucket } public  static  class  StrokeStrategyFactory {     public  static  IStrokeStrategy CreateStrokeStrategy (StrokeWith stroke )     {         switch  (stroke)         {             case  StrokeWith.Pen:                 return  new  PenStrokeStrategy();             case  StrokeWith.Brush:                 return  new  BrushStrokeStrategy();             default :                 throw  new  NotSupportedException("不支持的描边方式" );         }     } } public  static  class  FillStrategyFactory {     public  static  IFillStrategy CreateFillStrategy (FillWith fill )       {         switch  (fill)         {             case  FillWith.Brush:                 return  new  BrushFillStrategy();             case  FillWith.Bucket:                 return  new  BucketFillStrategy();             default :                 throw  new  NotSupportedException("不支持的填充方式" );         }     } } public  class  Graphics {     private  IStrokeStrategy _strokeStrategy;     private  IFillStrategy _fillStrategy;     public  Graphics (IStrokeStrategy strokeStrategy,                      IFillStrategy fillStrategy )    {         this ._strokeStrategy = strokeStrategy;         this ._fillStrategy = fillStrategy;     } 	     public  void  SetStrokeStrategy (IStrokeStrategy strokeStrategy )     {         this ._strokeStrategy = strokeStrategy;     }     public  void  Stroke ()     {         this ._strokeStrategy.Stroke();     }     public  void  Fill ()     {         this ._fillStrategy.Fill();     } } class  Program {     static  void  Main (string [] args )     {         IStrokeStrategy strokeStrategy = StrokeStrategyFactory.CreateStrokeStrategy(StrokeWith.Pen);         IFillStrategy fillStrategy = FillStrategyFactory.CreateFillStrategy(FillWith.Bucket);         Graphics graphics = new  Graphics(strokeStrategy, fillStrategy);         graphics.Stroke();         graphics.Fill();         graphics.SetStrokeStrategy(StrokeStrategyFactory.CreateStrokeStrategy(StrokeWith.Brush));     } } 
 
3 状态模式 3.1 State概念 
定义:允许一个对象在其内部状态改变时改变它的行为,从而使对象看起来似乎修改了它的类。
优点:
解决switch-case、if-else带来的难以维护的问题; 
结构清晰,提高了扩展性; 
通过单例或享元可使状态在多个上下文间共享。 
 
缺点:
随着状态的扩展,状态类数量会增多; 
增加了系统复杂度,使用不当将会导致逻辑的混乱; 
不完全满足开闭原则。 
 
相比较策略模式:
强策略模式强调可以互换的算法;状态模式强调改变对象内部的状态来帮助控制自己的行为。 
策略模式中用户直接与具体算法交互,决定算法的替换,需要了解算法本身;状态模式中状态是对象内部流转,用户不会直接跟状态交互,不需要了解状态本身。 
策略模式策略类不需要持有Context 的引用。状态模式中状态类需要持有Context 的引用,用来实现状态转移。 
 
 
3.2 有限状态机 
有限状态机:发生事件(event)后,根据当前状态(cur_state) ,决定执行的动作(action) ,并设置下一个状态(nxt_state) 。
有限状态机对象:
Context :上下文环境,定义客户程序需要的接口,并维护一个具体状态角色的实例,将与状态相关的操作委托给当前的 ConcreteState对象来处理; 
State :抽象状态,定义特定状态对应行为的接口; 
ConcreteState :具体状态,实现抽象状态定义的接口。 
 
案例:红绿灯。同时在状态机中配合了享元模式对性能进行优化(不然要多次new对象)。
注意:在状态机中使用享元模式,尽量不要将资源共享,不然在不同生态之间使用同一份资源可能会发生意想不到的结果。
 
public  class  GreenState  : TrafficLightState {     public  override  void  Handle (TrafficLight light )     {         Console.WriteLine("绿灯行" );         light.SetState(LightStateFactory.GetLightState<YellowState>());     } } public  class  RedState  : TrafficLightState {     public  override  void  Handle (TrafficLight light )     {         Console.WriteLine("红灯停" );         light.SetState(LightStateFactory.GetLightState<GreenState>());     } } public  class  YellowState  : TrafficLightState {     public  override  void  Handle (TrafficLight light )     {         Console.WriteLine("黄灯亮了等一等" );         light.SetState(LightStateFactory.GetLightState<RedState>());     } } public  abstract  class  TrafficLightState {     public  abstract  void  Handle (TrafficLight light ) ; } public  class  TrafficLight {     private  TrafficLightState _currentState;     public  TrafficLight ()     {         _currentState = new  RedState();     }     public  void  Turn ()     {         if  (_currentState != null )         {             _currentState.Handle(this );         }     }     public  void  SetState (TrafficLightState state )     {         _currentState = state;     } } public  class  LightStateFactory {     private  static  readonly  IDictionary<Type, TrafficLightState> _lightStates         = new  Dictionary<Type, TrafficLightState>();     private  static  readonly  object  _locker = new  object ();     public  static  TrafficLightState GetLightState <TLightState >() where  TLightState : TrafficLightState     {         Type type = typeof (TLightState);         if  (!_lightStates.ContainsKey(type))         {             lock  (_locker)             {                 if  (!_lightStates.ContainsKey(type))                 {                     TrafficLightState typeface = Activator.CreateInstance(typeof (TLightState)) as  TrafficLightState;                     _lightStates.Add(type, typeface);                 }             }         }         return  _lightStates[type];     } } class  Program {     static  void  Main (string [] args )     {         TrafficLight light = new  TrafficLight();         light.Turn();         light.Turn();         light.Turn();         light.Turn();     } } 
 
3.3 游戏玩家状态机 
案例:定义游戏玩家的状态,比如:走路,跑步等。通过状态机进行控制。
注:下列代码是在Unity中执行。
 
public  class  PlayerIdleState  : PlayerGroundState {          public  PlayerIdleState (Player _player, PlayerStateMachine _stateMachine, string  _animBoolName ) : base (_player, _stateMachine, _animBoolName )     {     }     public  override  void  Enter ()     {                  base .Enter(); 		         player.ZeroVelocity();     }     public  override  void  Exit ()     {         base .Exit();     }     public  override  void  Update ()     {         base .Update();                  if  (xInput == player.facingDir && player.IsWallDetected())             return ;         if  (xInput != 0  && !player.isBusy)             stateMachine.ChangeState(player.moveState);     } } public  class  PlayerGroundState  : PlayerState {     public  PlayerGroundState (Player _player, PlayerStateMachine _stateMachine, string  _animBoolName ) : base (_player, _stateMachine, _animBoolName )     {     }     public  override  void  Enter ()     {         base .Enter();     }     public  override  void  Exit ()     {         base .Exit();     }     public  override  void  Update ()     {         base .Update();         if  (Input.GetKey(KeyCode.Mouse0))             stateMachine.ChangeState(player.primaryAttack);         if  (!player.IsGroundDetected())             stateMachine.ChangeState(player.airState);         if  (Input.GetKeyDown(KeyCode.Space) && player.IsGroundDetected())             stateMachine.ChangeState(player.jumpState);     } } public  class  PlayerState {     protected  PlayerStateMachine stateMachine;     protected  Player player;     protected  float  xInput;     protected  float  yInput;     private  string  animBoolName;          public  PlayerState (Player _player, PlayerStateMachine _stateMachine, string  _animBoolName )     {         this .player = _player;         this .stateMachine = _stateMachine;         this .animBoolName = _animBoolName;     } 	     public  virtual  void  Enter ()     {         player.animator.SetBool(animBoolName, true );         rb = player.rb;         triggerCalled = false ;     }     public  virtual  void  Update ()     {         stateTimer -= Time.deltaTime;         xInput = Input.GetAxisRaw("Horizontal" );         yInput = Input.GetAxisRaw("Vertical" );         player.animator.SetFloat("yVelocity" , rb.velocity.y);     }     public  virtual  void  Exit ()     {         player.animator.SetBool(animBoolName, false );     } } public  class  PlayerStateMachine {          public  PlayerState currentState { get ; private  set ; } 	     public  void  Initialize (PlayerState _startState )     {         currentState = _startState;         currentState.Enter();     } 	     public  void  ChangeState (PlayerState _newState )     {         currentState.Exit();         currentState = _newState;         currentState.Enter();     } } using  System.Collections;using  System.Collections.Generic;using  UnityEngine;public  class  Player  : MonoBehaviour {          public  PlayerStateMachine stateMachine { get ; private  set ; } 	     public  PlayerIdleState idleState { get ; private  set ; }     public  PlayerMoveState moveState { get ; private  set ; }     public  PlayerJumpState jumpState { get ; private  set ; }               public  Animator animator { get ; private  set ; }     private  void  Awake ()     {                  stateMachine = new  PlayerStateMachine();         idleState = new  PlayerIdleState(this , stateMachine, "Idle" );         moveState = new  PlayerMoveState(this , stateMachine, "Move" );         jumpState = new  PlayerJumpState(this , stateMachine, "Jump" );     } 	     private  void  Start ()     {         stateMachine.Initialize(idleState);     } 	     private  void  Update ()     {         stateMachine.currentState.Update();     } } 
 
4 观察者模式 4.1 Observer概念 
定义:多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
案例:聊天应用程序、消息推送服务、消息队列。
通知模式:
 
4.2 标准观察者模式 
观察者对象:
Subject :抽象主题角色,它是一个抽象类(而实际上我用的是普通类),提供了一个用于保存观察者对象的集合和增加、删除以及通知所有观察者的方法。 
ConcreteSubject :具体主题角色。 
IObserver :抽象观察者角色,它是一个接口,提供了一个更新自己的方法,当接到具体主题的更改通知时被调用。 
Concrete Observer :具体观察者角色,实现抽象观察者中定义的接口,以便在得到主题的更改通知时更新自身的状态。 
 
案例:在中学阶段有一篇课文《口技》,其中有一句“遥闻深巷中犬吠,便有妇人惊觉欠伸,其夫呓语。既而儿醒,大啼。夫亦醒。”
由题可知,狗叫会引发妇人惊觉、夫呓语、儿大啼,而儿大啼又会引发夫亦醒。所以在这段关系中,狗是纯被观察者,负责发出狗叫的动作。妇人和夫是纯观察者,妇人观察儿子,夫观察儿子和狗。儿子既是观察者又是被观察者,观察狗的动作的同时被父亲观察。
优点:
主题与观察者之间的耦合低。 
主题与观察者之间建立了一套触发机制,使得主题状态改变时能够及时通知观察者。 
 
缺点:
主题与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用。 
当观察者对象很多时,事件通知会花费很多时间,影响程序的效率。 
 
 
public  class  Dog : Subject {     public  void  Bark ()     {         Console.WriteLine("遥闻深巷中犬吠" );         Publish(new  EventData { Source = this , EventType = "DogBark"  });     } } public  class  Son  : Subject , IObserver {     public  void  Update (EventData eventData )     {         if  (eventData.EventType == "DogBark" )         {             Wakeup();         }     }     public  void  Wakeup ()     {         Console.WriteLine("既而儿醒,大啼" );         Publish(new  EventData { Source = this , EventType = "SonCry"  });     } } public  class  Wife  : IObserver {     public  void  Update (EventData eventData )     {         if  (eventData.EventType == "DogBark" )         {             Wakeup();         }     }     public  void  Wakeup ()     {         Console.WriteLine("便有妇人惊觉欠伸" );     } } public  class  Husband  : IObserver {     public  void  DreamTalk ()     {         Console.WriteLine("其夫呓语" );     }     public  void  Update (EventData eventData )     {         if  (eventData.EventType == "DogBark" )         {             DreamTalk();         }         else  if  (eventData.EventType == "SonCry" )         {             Wakeup();         }     }     public  void  Wakeup ()     {         Console.WriteLine("夫亦醒" );     } } public  class  EventData {          public  object  Source { get ; set ; } 	     public  string  EventType { get ; set ; } } public  class  Subject {     private  readonly  IList<IObserver> _observers = new  List<IObserver>();     public  void  AddObserver (IObserver observer )     {         _observers.Add(observer);     }     public  void  RemoveObserver (IObserver observer )     {         _observers.Remove(observer);     }     public  void  Publish (EventData eventData )     {         foreach  (var  observer in  _observers)         {             observer.Update(eventData);         }     } } public  interface  IObserver {     void  Update (EventData eventData ) ; } class  Program {     static  void  Main (string [] args )     {         Dog dog = new  Dog();         Wife wife = new  Wife();         Husband husband = new  Husband();         Son son = new  Son();         dog.AddObserver(wife);         dog.AddObserver(husband);         dog.AddObserver(son);         son.AddObserver(husband);         dog.Bark();     } } 
 
4.3 事件总线 
以下是观察者模式使用的两种典型场景,它们有何不同:
回顾前面例子中的两个问题:
dog.AddObserver(...)真的合适吗?实际生活中,狗真的有这种能力吗?狗叫像是一种广播,而不需要专门的观察者。 
因为C#中不支持多继承,如果Dog本身继承自Animal的基类,如果同时作为被观察者,除了用上述演进一的实现,还能如何实现? 
 
解决办法:将Subject做成抽象类ISubject,然后再设置一个Subject实现ISubject,将Subject作为事件总线。之后在被观察者的代码中可以以组合的方式加入Subject。使用时,只需要设置一个总线,初始化被观察对象时设置总线,同时向总线中加入观察者。
 
public  class  Dog {     private  readonly  ISubject _subject;     public  Dog (ISubject subject )     {         this ._subject = subject;     }     public  void  Bark ()     {         Console.WriteLine("遥闻深巷中犬吠" );         _subject.Publish(new  EventData { Source = this , EventType = "DogBark"  });     } } public  class  Son : IObserver {     private  readonly  ISubject _subject;     public  Son (ISubject subject )     {         this ._subject = subject;     }     public  void  Update (EventData eventData )     {         if  (eventData.EventType == "DogBark" )         {             Wakeup();         }     }     public  void  Wakeup ()     {         Console.WriteLine("既而儿醒,大啼" );         _subject.Publish(new  EventData { Source = this , EventType = "SonCry"  });     } } public  class  Husband : IObserver {     public  void  DreamTalk ()     {         Console.WriteLine("其夫呓语" );     }     public  void  Update (EventData eventData )     {         if  (eventData.EventType == "DogBark" )         {             DreamTalk();         }         else  if  (eventData.EventType == "SonCry" )         {             Wakeup();         }     }     public  void  Wakeup ()     {         Console.WriteLine("夫亦醒" );     } } public  class  Wife : IObserver {     public  void  Update (EventData eventData )     {         if  (eventData.EventType == "DogBark" )         {             Wakeup();         }     }     public  void  Wakeup ()     {         Console.WriteLine("便有妇人惊觉欠伸" );     } } public  class  EventData {          public  object  Source { get ; set ; } 	     public  string  EventType { get ; set ; } } public  class  Subject : ISubject {     private  readonly  IList<IObserver> _observers = new  List<IObserver>();     public  void  AddObserver (IObserver observer )     {         _observers.Add(observer);     }     public  void  RemoveObserver (IObserver observer )     {         _observers.Remove(observer);     }     public  void  Publish (EventData eventData )     {         foreach  (var  observer in  _observers)         {             observer.Update(eventData);         }     } } public  interface  ISubject {     void  AddObserver (IObserver observer ) ;     void  RemoveObserver (IObserver observer ) ;     void  Publish (EventData eventData ) ; } public  interface  IObserver {     void  Update (EventData eventData ) ; } class  Program {     static  void  Main (string [] args )     {    		         ISubject subject = new  Subject();                  Dog dog = new  Dog(subject);         Wife wife = new  Wife();         Husband husband = new  Husband();                  Son son = new  Son(subject); 		         subject.AddObserver(wife);         subject.AddObserver(husband);         subject.AddObserver(son);         dog.Bark();     } } 
 
4.4 消息队列 
思考:如果主题和观察者分属两个不同的系统该怎么办?
利用Broker代理:
一个Queue<EventData>类型的队列,用于存放事件消息。 
一组注册和注销观察者的方法。 
一个接收来自事件发布者的事件消息的方法。 
最后就是事件消息的通知机制,这里用的是定时轮询的方式,实际应用中肯定不会这么简单。 
 
演进过程:
第一阶段主题与观察者之间的耦合低,但并没有完全解耦,这种情况主要应用在类似报纸订阅的场景。 
第二阶段在主题与观察者之间加了一条总线,使得主题与观察者完全解耦,这种情况主要运用在类似黑板广播消息的场景。 
第三阶段在总线与观察者之间加了一个代理,使得存在于不同系统之间的主题与观察者也能够解耦并且正常通信。 
 
.Net中的应用:
委托(delegate)和事件(event)。 
.Net中提供了一组泛型接口IObserver<T>和IObservable<T>可用于实现事件通知机制。 
 
 
...; public  class  Broker {      private  static  readonly  Lazy<Broker> _instance         = new  Lazy<Broker>(() => new  Broker());          private  readonly  Queue<EventData> _eventDatas = new  Queue<EventData>();     private  readonly  IList<IObserver> _observers = new  List<IObserver>();     private  readonly  Thread _thread;     private  Broker ()     {                  _thread = new  Thread(Notify);         _thread.Start();     }     public  static  Broker Instance     {         get { return  _instance.Value; }     }     public  void  AddObserver (IObserver observer )     {         _observers.Add(observer);     }     public  void  RemoveObserver (IObserver observer )     {         _observers.Remove(observer);     } 	     private  void  Notify (object ? state )     {         while  (true )         {             if  (_eventDatas.Count > 0 )             {                 var  eventData = _eventDatas.Dequeue();                 foreach  (var  observer in  _observers)                 {                     observer.Update(eventData);                 }             }             Thread.Sleep(1000 );         }     }     public  void  Enqueue (EventData eventData )     {         _eventDatas.Enqueue(eventData);     } } public  class  Subject : ISubject {     public  void  Publish (EventData eventData )     {         Broker.Instance.Enqueue(eventData);     } } public  interface  ISubject {     void  Publish (EventData eventData ) ; } class  Program {     static  void  Main (string [] args )     {         ISubject subject = new  Subject();         Dog dog = new  Dog(subject);         Wife wife = new  Wife();         Husband husband = new  Husband();         Son son = new  Son(subject);                  Broker.Instance.AddObserver(wife);         Broker.Instance.AddObserver(husband);         Broker.Instance.AddObserver(son);         dog.Bark();     } } 
 
5 责任链模式 5.1 ChainOfResponsibility概念 
定义:多个对象都有机会处理某个请求,将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。
案例:用程序实现一个请假流程,根据请假天数不同,需要各级领导审批,例如,如果请假时长不超过1天,则直接团队负责人申请即可,超过1天不超过3天则需要项目经理审批通过才行,而超过3天不超过7天则需要CTO审批等等。
优点:请求和处理分离,请求者可以不用知道是谁处理的,处 理者可以不用知道请求的全貌,两者解耦,提高系统的灵活性;
缺点:性能不高;调试不很方便。
 
==后面的作者鸽了== 6 迭代器模式 6.1 Iterator概念 7 命令模式 7.1 Command概念 8 备忘录模式 8.1 Memento概念 9 访问者模式 9.1 Visitor概念 10 中介者模式 11 解释器模式 11.1 Interpreter概念  
五、其他 1 反模式 
设计模式终章之反模式 - 知乎 (zhihu.com)