结构模式

门面模式

意图:

为子系统中的一组接口提供一个一致的界面, façade 模式定义了一个高层接口,这个接口使得这一子系统更加容易使用

适用:

Ø         让调用者简单:不需要使用一个复杂系统得所有功能,并且可以创建一个新的类来包容访问原来系统所有使用规则。

Ø         增加系统的独立性:希望包装或者隐藏原有系统,原有系统不要直接和客户层交互,提高子系统的独立性和可维护性。

Ø         层次化结构:在需要构建一个层次结构的系统时,使用门面模式可以简化层与层之间的依赖关系

 

体现的设计原则

Ø         接口隔离原则

Ø         分解调用者和被调用者 ( 解耦 )

 

技术

Ø         门面对象引用被调用对象,他们之间是通过委托技术实现解耦

 

区别联系

Ø         和适配器的相似点:他们都是处理接口问题、他们都不能给接口添加功能、他们都能包装一个或者多个类;不同点:门面模式是隔离调用和被调用、隐藏接口数量,适配器是转换接口让他们协同工作;它们依赖的技术原则也不同

推演

Ø         从意图中“使得这一子系统更加容易使用”这句话,更加易于使用应该包含了两层含义,这在使用环境中也能体现,一、是使对外接口数量尽可能少,二、是解耦不同层之间依赖关系

Ø         如果推演应该围绕这两个要求分别推演

总结

Ø         不要在门面模式中增加功能,否则导致结构混乱,维护困难,如果需要增加功能可以通过别的模式实现(装饰模式)

Ø         模式思想这本书中谈到包可以部分实现门面模式的意图,我认为的确是这样,实现了接口隔离原则,但是分解调用者和被调用者这个原则包无法实现,模式思想中可能忽略了这个适用环境,从他的标题可以看到

Ø         这个模式减少了对象之间依赖,会降低维护成本,但是会导致更多包装类被制造出来,以处理和其他组件的沟通,这可能会导致复杂度和开发时间的增加,并且降低运行时性能

适配器模式

意图

Ø         将一个类的接口转换成客户希望的另外一个接口,适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作

 

适用

Ø         使用一个已经存在的类,而它的接口不符合你的需要

Ø         创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类协同工作(不怎么理解)

Ø         在设计里,需要改变多个已经存在的子类的接口,如果使用类适配器需要对每个子类定义一个适配器,这显然不实际

 

原则

Ø         合成 / 聚合原则

Ø         依赖倒转原则

 

技术

Ø         继承技术:得到源的方法和类型

Ø         委托技术:把方法实现交给源

区别联系

Ø         装饰模式和适配器模式都是包装模式,都可以再次被包装。装饰模式只增加接口功能,不转变接口,而适配器模式只转变接口而不增加接口功能

推演

Ø         适配器模式的本质是转变接口,让原来不能协同工作的对象现在可以协同工作,虽然适用环境不同,但是本质一致

 

         class  Customer { 

           public  static  final  String  US = "US"; 

           public  static  final  String  CANADA = "Canada"; 

           private  String  address; 

           private  String  name; 

           private  String  zip, state, type; 

           public  boolean  isValidAddress() { 

                   … 

                   … 

           } 

           public  Customer(String  inp_name, String  inp_address, 

                           String  inp_zip, String  inp_state, 

                           String  inp_type) { 

             name = inp_name; 

             address = inp_address; 

             zip = inp_zip; 

             state = inp_state; 

             type = inp_type; 

           } 

         }//end of class 

         public  interface  AddressValidator { 

           public  boolean  isValidAddress(String  inp_address, 

              String  inp_zip, String  inp_state); 

         }//end of class 

         class  USAddress implements  AddressValidator { 

           public  boolean  isValidAddress(String  inp_address, 

              String  inp_zip, String  inp_state) { 

            if  (inp_address.trim().length () < 10) 

              return  false

            if  (inp_zip.trim().length () < 5) 

              return  false

            if  (inp_zip.trim().length () > 10) 

              return  false

            if  (inp_state.trim().length () != 2) 

              return  false

            return  true

           } 

         }//end of class

         class  Customer { 

                   …            … 

          public  boolean  isValidAddress() { 

            //get an appropriate address validator 

            AddressValidator validator = getValidator(type); 

            //Polymorphic call to validate the address 

            return  validator.isValidAddress(address, zip, state); 

          } 

          private  AddressValidator getValidator(String  custType) { 

            AddressValidator validator = null

            if  (custType.equals(Customer.US)) { 

              validator = new  USAddress(); 

            } 

            return  validator; 

          } 

         }//end of class 

 

         但是当验证来自加拿大的客户时,就要对应用进行改进。这需要一个验证加拿大客户地址的验证类。让我们假设已经存在一个用来验证加拿大客户地址的使用工具类 CAAddress

         class  CAAddress { 

           public  boolean  isValidCanadianAddr(String  inp_address, 

              String  inp_pcode, String  inp_prvnc) { 

            if  (inp_address.trim().length () < 15) 

              return  false

            if  (inp_pcode.trim().length () != 6) 

              return  false

            if  (inp_prvnc.trim().length () < 6) 

              return  false

            return  true

           } 

         }//end of class 

CAAdress 类提供了一个 isValidCanadianAddr 方法,但是 Customer 期望一个声明在 AddressValidator 接口中的 isValidAddress 方法。   接口的不兼容使得 Customer 对象利用现有的 CAAdress 类是困难的。一种意见是改变 CAAdress 类的接口,但是可能会有其他的应用正在使用 CAAdress 类的这种形式。改变 CAAdress 类接口会影响现在使用 CAAdress 类的客户。   应用适配器模式,类适配器 CAAdressAdapter 可以继承 CAAdress 类实现 AddressValidator 接口。

         public  class  CAAddressAdapter extends  CAAddress 

           implements  AddressValidator { 

           public  boolean  isValidAddress(String  inp_address, 

              String  inp_zip, String  inp_state) { 

             return  isValidCanadianAddr(inp_address, inp_zip, 

                    inp_state); 

           } 

         }//end of class 

         因为适配器 CAAdressAdapter 实现了 AddressValidator 接口,客户端对象访问适配器 CAAdressAdapter 对象是没有 任何问题的。当客户对象调用适配器实例的 isValidAddress 方法的时候,适配器在内部把调用传递给它继承的 isValidCanadianAddr 方法。   在 Customer 类内部, getValidator 私有方法需要扩展,以至于它可以 在验证加拿大客户的时候返回一个 CAAdressAdapter 实例。返回的对象是多态的, USAddress CAAddressAdapter 都实现 AddressValidator 接口,所以不用改变。

         class  Customer { 

                   … 

                   … 

           public  boolean  isValidAddress() { 

             //get an appropriate address validator 

             AddressValidator validator = getValidator(type); 

             //Polymorphic call to validate the address 

             return  validator.isValidAddress(address, zip, state); 

           } 

           private  AddressValidator getValidator(String  custType) { 

             AddressValidator validator = null

             if  (custType.equals(Customer.US)) { 

               validator = new  USAddress(); 

             } 

             if  (type.equals(Customer.CANADA)) { 

               validator = new  CAAddressAdapter(); 

             } 

             return  validator; 

           } 

         }//end of class 

 

总结

Ø         适配器模式分类适配器和对象适配器,他们的比较如下:对象适配器不仅可以适配某个类,也可以适配该类的任何子类,而类适配器只能适配一个类;对象适配器是使用委托,而类适配器使用继承

 

 

装饰模式

意图

Ø         动态地给一个对象添加一些额外的职责

适用

Ø         在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责

Ø         处理那些可以撤销的职责

Ø         当不能采用生成子类的方法进行扩展时。

 

原则

Ø         开闭原则

 

技术

Ø         继承:得到相同的类型,在实例化时需要

Ø         委托:增加功能

区别联系

Ø         属于包装模式,但是包装的目的不一样,装饰模式是为了增加功能,使用的技术和适配器很相似,所以他们模式结构图类似,但是在方法内部是不同的。

推演

         抽象构件

         public abstract class AbstractCellPhone

             {

                 public abstract string CallNumber();

                 public abstract string SendMessage();

         }

         两个具体构件

         public class NokiaPhone : AbstractCellPhone    {

                 public override string CallNumber()        {

                     return "NokiaPhone call sombody";

                 } 

                 public override string SendMessage()        {

                     return "NokiaPhone send a message to somebody";

                 }

             } 

             public class MotoPhone : AbstractCellPhone    {

                 public override string CallNumber()        {

                     return "MotoPhone call sombody";

                 } 

                 public override string SendMessage()        {

                     return "MotoPhone send a message to somebody";

                 }

          }   

         抽象装饰类

         public abstract class Decorator:AbstractCellPhone    {

                 AbstractCellPhone _phone; 

                 public Decorator(AbstractCellPhone phone)        {

                     _phone = phone;

                 } 

                 public override string CallNumber()        {

                     return _phone.CallNumber();

                 } 

                 public override string SendMessage()        {

                     return _phone.SendMessage();

                 }

           }

         具体装饰类

         public class DecoratorGPS : Decorator    {

                 public DecoratorGPS(AbstractCellPhone phone)

                     : base(phone)

                 { } 

                 public override string CallNumber()        {

                     return base.CallNumber() + " with GPS";

                 } 

                 public override string SendMessage()        {

                     return base.SendMessage() + " with GPS";

                 }

             }

         具体装饰类

         public class DecoratorBlueTooth : Decorator    {

                 public DecoratorBlueTooth(AbstractCellPhone phone)

                     : base(phone)

                 { } 

                 public override string CallNumber()        {

                     return base.CallNumber() + " with BlueTooth";

                 } 

                 public override string SendMessage()        {

                     return base.SendMessage() + " with BlueTooth";

                 }

          }

         static void Main(string[] args)

                 {

                      AbstractCellPhone phone = new NokiaPhone();

                     Console.WriteLine(phone.CallNumber());

                     Console.WriteLine(phone.SendMessage());

                     DecoratorGPS gps = new DecoratorGPS(phone);     //add GSP

                     Console.WriteLine(gps.CallNumber());

                     Console.WriteLine(gps.SendMessage());

                     DecoratorBlueTooth bluetooth = new DecoratorBlueTooth(gps); //add GSP and bluetooth

                     Console.WriteLine(bluetooth.CallNumber());

                     Console.WriteLine(bluetooth.SendMessage());

                     Console.Read();

              }

         结果

         NokiaPhone call sombody

         NokiaPhone send a message to somebody

         NokiaPhone call sombody with GPS

         NokiaPhone send a message to somebody with GPS

         NokiaPhone call sombody with GPS with BlueTooth

         NokiaPhone send a message to somebody with GPS with BlueTooth

 

Ø         上面的例子我们可以用文字来表达:手机可以先写文字或者画图片,然后通过蓝牙或者 GMS 发送出去,如果还是用过程的方式变程,则应该是这样

Class 手机 {

写文本() { 返回发送对象 }

蓝牙发送(发送对象) {}

GMS 发送(发送对象) {}

}

Class client{

       四种组合

}

现在如果增加新类型的发送对象,或者新的发送方式都要修改手机类,因为这是两个变化的因素,所以要分开这两个变化因素。分析到这一步,可以看到他和桥模式是一致的,分开两个变化因素,但是他们两个变化因素本身不一样,这两个因素之间关系不一样,装饰模式两个变化因素是修饰和被修饰关系,而桥模式两个因素之间关系是拥有和被拥有关系。这是导致他们朝不同方向发展的决定因素。那么进行如下改造

 

Class 手机 {

处理短信() {} }

Class 诺基亚手机 {

处理短信() { 返回短信内容 } }

Class 发送短信 {

       手机:手机实体

       处理短信() { }

}

Class 蓝牙发送短信 extend  发送短信 {  

       处理短信() { 发短信 }

}

Class client{

       Class 手机 手机实体 =new 诺基亚手机()

       Class 发送短信 发送实体 =new 蓝牙发送短信();

       发送实体。手机实体 = 手机实体;

       发送实体。处理短信()    

}

装饰者和被装饰者应该是同一类型的,所以加了一个接口继承

总结

Ø         利用继承设计子类行为是在编译时静态决定的,而且所有子类都会继承到相同行为。如果利用组合的做法扩展对象行为,就可以在运行时动态地进行扩展

Ø         继承是为了有正确的类型,而不是继承它的行为。行为来自装饰者和基础组建,或者与其他装饰者之间的组合关系

Ø         装饰模式一般是工厂或者生成器这样的模式创建的

Ø         装饰者对组件的客户是透明的,除非客户需要依赖具体

Ø         新增一种功能只要增加一个类,如果不使用装饰类则要形成 2*n 个类( n 代表功能)

 

代理

意图

为其他对象提供一种代理以控制对这个对象的访问

适用

Ø         远程代理

Ø         虚代理

Ø         保护代理

Ø         只能代理

原则

 

 

技术

Ø         继承:让被代理者和代理者都继承于一个抽象类

Ø         委托:代理类委托被代理类实现接口

 

区别联系

Ø         代理和装饰都是对对象包装,但是代理是控制对象访问,而装饰是增加对象功能,代理是代表对象;代理和适配器

推演

Ø         客户类本身提供方法检索客户,但是需要根据检索者身份来确定可以检索哪些客户。

 

Class 客户 {

       查询() {}

}

如果需要根据客户身份检索,则每次有新身份添加,或者对原来身份检索权变更都要修改客户类,这显然不满足开闭原则,另外也不符合我们说的把变化的和不变的分离

所以我们进行如下扩展,添加一个客户代理类,

 

Class 客户代理 {

       调用客户中的查询,并把下面的条件加上() {}

       得到该身份所有客户的条件() {}

}

这样用户在查询客户时则加上了自身身份条件

总结

Ø         代理模式有时会实例化对象

Ø         和其他包装模式一样,代理模式会增加类的数量

 

桥模式

意图

将抽象部分和实现部分分离,使他们都可以独立地变化

适用

Ø         如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的联系

Ø         设计要求实现化角色的任何改变不应当影响客户端,或者说实现化角色的改变对客户端是完全透明的

Ø         一个构件有多于一个的抽象化角色和实现化角色,系统需要它们之间进行动态耦合。

Ø         虽然在系统中使用继承是没有问题的,但是由于抽象化角色和具体化角色需要独立变化,设计要求需要独立管理这两者

原则

Ø         合成 / 聚合原则

Ø         解耦

技术

Ø         继承:具有相同类型,提供不同行为的基础

Ø         委托:将实现委托给另外对象,增加变化的灵活性

Ø         抽象:通过抽象层的引用来控制子层行为

区别联系

Ø         和上面结构模式结构模式区别很大,他应该不是包装模式,他通过把职责分配出去,对象自身可以变化(保持相同类型)实现双方独立变化,而不相互影响,他是把一个对象分成两个对象,然后再对这两个对象进行抽象,得到抽象层和实现层。

推演

Ø         假设需要制造多种品牌汽车

 

Class 汽车 {

       制造福特汽车() {}

       制造别克汽车() {}

}

这样存在问题很明显,如果需要增加一个新的制造商品牌则需要修改原来的类,这不符合我们的设计原则。所以我们进行如下改造

Class 汽车 {

       制造车厂:工厂

       工厂。制造汽车() {}      

}

Class 制造车厂 {

       制造汽车() {}

}

Class 福特工厂 extend 制造车厂 {

       制造汽车() {}

}

Class 别克工厂 extend 制造车厂 {

       制造汽车() {}

}

经过上面的改造如果需要增加新的汽车制造厂则容易多了,直接继承制造汽车厂,如下:

Class client{

Class 汽车。工厂 =new 福特工厂()

汽车。工厂。制造汽车()

Class 汽车。工厂 =new 别克工厂()

汽车。工厂。制造汽车()

}

但如果要新增制造卡车,则需要改变汽车类结构,否则又需要修改类,改造如下

Class {

制造车场:工厂

工厂。造车() {}

}

Class 汽车 extend {

制造车场:工厂

工厂。造车() {}

}

Class 卡车 extend {

制造车场:工厂

工厂。造车() {}

}

 

Class 制造车厂 {

       制造汽车() {}

       制造卡车() {}

}

Class 福特工厂 extend 制造车厂 {

       制造汽车() {}

       制造卡车() {}

}

Class 别克工厂 extend 制造车厂 {

       制造汽车() {}

       制造卡车() {}

}

 

Class client{

Class 汽车。工厂 =new 福特工厂()

汽车。工厂。制造汽车()

Class 汽车。工厂 =new 别克工厂()

汽车。工厂。制造汽车()

 

Class 卡车。工厂 =new 福特工厂()

汽车。工厂。制造卡车()

Class 卡车。工厂 =new 别克工厂()

汽车。工厂。制造卡车()

 

}

总结

Ø         将实现予以解耦,让它和界面之间不再永久绑定

Ø         抽象和实现之间可以独立扩展,不会影响到对方

Ø         对于具体的抽象类所作的改变,不会影响到客户

Ø         桥接模式增加了系统的复杂度

Ø         从推演可以看到这个对象的职责委托给另外一个对象了,这是为了方便职责的变化,另外这个对象本身有可能要发生变化,所以又给这个对象加了一个抽象层

 

欢迎批评

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值