本章为对5.2节的一个总结,便于日后复习。
1 Creational patterns(关于如何“创建类的新实例”)
1)Factory Method pattern(工厂方法模式)
“除了使用new操作符之外,还有更多制造对象的方法,你将了解到这个活动不应该总是公开地进行,也会认识到初始化经常造成“耦合”问题。”——《Design Patterns》
工厂方法模式就像一个“虚拟构造器”,通过定义一个创建对象的接口,让该接口的子类型来决定实例化哪一个类,从而使一个类的实例化延迟到其子类。
当client不知道或者不确定要创建哪个具体类的实例,或者不想再client代码中知名要具体创建的实例是,可以使用工厂方法。
我们更加熟悉的是静态工厂方法,但前者并不属于工厂方法。
静态工厂方法
静态工厂方法既可以在ADT内部实现,也可以构造单独的工厂类。
比如Collections.sort()
与通过构造器创建对象相比:
- 静态工厂方法可以具有指定的名称
- 不必在每次调用的时候都创建新对象
- 由2可知,不能通过继承来改变创建方法的行为
- 可以返回原返回类型的任意子类型,满足开闭原则
工厂方法的优点:
1.可以防止new的滥用,减少内存占用,对于客户端而言,调用某一个方法来直接获得一个实例,更加方便,简洁,直观。
2.同时,在有新的具体产品类加入时,可以增加新的工厂类或修改已有工厂类,这样不会影响客户端代码,符合开闭原则,即对于扩展是开放的,而对于修改是封闭的(OCP)。
3.另外,也可以根据类型决定创建哪个具体产品。
缺点:每增加一种产品就需要增加一个新的工厂子类,比较麻烦。
2)抽象工厂模式
背景联系:一个UI,包含多个创口空间,这些控件的组合在不同的OS中实现不同。又,一个只能房屋的设备管理系统,要控制多个设备,这些设备的制造商各有不同,控制接口有差异。
就像造手机,每个材料厂商都可以提供一些材料,而一部手机可能会从几个材料商中分别取一些材料来创建一部手机,对于一部手机,材料的搭配是固定的
内容:就是提供一个创建一系列相关或相互依赖对象的接口,而不需要指定它们具体的类。
途径:通过一个可调用其他多个工厂的工厂(手机厂商)。
本质上,抽象工厂是把多类产品的工厂方法组合在了一起,它创建的不是一个完整产品。而是一个产品族,且其中每一个产品的搭配都是固定的,所以如果直接使用多个工厂方法由用户自己来构建产品,那么很有可能出现搭配错误。
抽象工厂和抽象方法相比:
- 前者可以创建多个类型的对象,而后者只能创建某个类型的对象
- 前者使用代理,而后者使用继承。
2 Structural patterns(结构模式)
1)代理模式
背景:某个对象比较“敏感”,“私密”,“贵重”,且不希望用户直接访问,那么这是可以设置一个proxy,在二者之间建立防火墙。
代理分为三种类型:
- 远程代理:为一个对象在不同的地址空间提供局部代理(缓存机制)
- 虚代理:根据需要创建开销很大的对象
- 保护代理:提供提供访问保护。
该模式与适配器模式其实在一定意义上,都是对某些类进行一个包装,它们的区别在于目的不同:
适配器模式的目的是为了消除不兼容,目的是B以客户端期望的统一方法与A建立起联系。。如果没有适配器,可能得改不少地方,为了方便,为了适配。
代理模式的目的是为了隔离对复杂对象的访问,降低难度和代价,定位在“访问、使用行为”。
3 Behavioral patterns(行为模式)
1)Observer(观察者模式)
背景:依赖的一方必须和被依赖方的状态一直。类似粉丝与明星的关系。
方案:可以通过一种“发布-订阅”的形式,发布方的变化会通知订阅方,订阅方在发布方处注册,通过接口分离两者。
看懂这个图,意思大概就能明白了。
三种变体
- 发生变化时推送通知
- 发生变化时推送通知和数据(上图应该就属于这种,即有推送通知又可以获得subject的状态)
- 拉取数据(这个就是observer可以主动获得subject的状态)
优点
- 主体与observer之间松耦合,主体不需要关注观察者(加个列表便于管理)
- 可以动态的增加和删除观察者(subject内部写一个动态调度方法?)
- 观察者的行为不受主体的控制
实现的一些要点:
- 主体内部需要一个数据结构去存储观察者列表
- 可以通过在update中增加参数来观察多个主体
- 触发机制:subject的setstate可以自己决定,比如延时发送,比如加一些前缀后缀(应该是这个意思吧?)
2)访问者模式(visitor)
背景:Decouple the operations from the object structure,就是将数据和数据上的一些特定操作分离开来。为ADT预留一个将来可扩展功能的“接入点”,外部实现的功能代码可以在不改变ADT本身的情况下在需要时通过委托机制接入ADT。
实现:在数据所在类的内部创建
描述:一个accept方法用于设置委托关系
几个需要对数据实现的操作,通过委托方式来实现。
因为具体操作的方法在外部,当修改这一方法时,无需修改ADT。、
比如对于一些航班信息,我需要打印,我可以横着打印,也可以竖着打印,这取决于visitor,从而实现了操作与数据的解耦。
而如果把操作的代码放在数据类的内部,那么在修改时就需要在ADT中修改了。
Visitor与Iterator的对比:
同:都是通过委托机制来实现对数据的操作
异:iterator是以遍历的方式来访问数据信息而不会暴露数据内部是如果表示的,也就是说RI是看不到的。而visitor侧重于对对数据操作的灵活性,可以很容易的更改操作代码而不影响ADT。
Stategy与Visitor的对比:
同:二者都是通过delegation来建立两个对象的动态联系,都很灵活。
异:visitor是站在外部client的角度而strategy是站在内部ADT的角度
前者:“我可以对数据做哪些操作?这取决于我,得益于visitor模式”
后者:“对于ADT中这些数据的某种操作,或者对于一个抽象结构的实现,我是否可以针对不同场景用最优的方法去实现?,这取决于外部环境,得益于策略模式”