Builder生成器模式(创建型模式)
一、动机
在软件系统中,有时候面临着“一个复杂对象”的创建工作,期通常由各个部分的子对象用一定的算法构成:由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。 如何提供一种“封装机制”来隔离出“复杂对象的各个部分”的变化,从而保持系统中的“稳定结构算法”不随着需求的改变而改变? 二、意图
将一个复杂对象的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。 三、要点
Builder模式主要用于“分步骤构建一个复杂的对象”。在这其中“分步骤”是一个比较稳定的算法,而复杂对象的各个部分则经常变化。
变化点在哪里,封装哪里——Builder模式主要在于应对“复杂对象各个部分”的频繁需求变动。其缺点在于难以应对“分步骤构建算法”的需求变动。
Abstract Factory模式解决“系列对象”的需求变化,Builder模式解决“对象部分”的需求变化。Builder模式通常和Composite模式组合使用。
abstract class House { } public abstract class Builder
{ public abstract void BuildDoor(); public abstract void BuildWall(); public abstract void BuildWindows(); public abstract void BuildFloor(); public abstract void BuildHouseCeiling(); public abstract House GetHouse(); } public class GameManager
{ public static House CreateHouse(Builder builder) { builder.BuildDoor(); builder.BuildDoor(); builder.BuildWindows(); builder.BuildWindows(); builder.BuildFloor(); builder.BuildWall(); builder.BuildWall();
builder.BuildHouseCeiling(); return builder.GetHouse(); } } public class RomainHouse : House { } public class RomainHouseBuilder : Builder { public override void BuildDoor() { } public override void BuildWall() { } public override void BuildWindows() { } public override void BuildFloor() { } public override void BuildHouseCeiling() { } public override House GetHouse(); }
Prototype原型模式(创建型模式)
一、依赖关系的倒置
抽象不应该依赖于实现细节,实现细节应该依赖于抽象。 二、动机
在软件系统中,经常面临着“某些结构复杂的对象”的创建工作:由于需求的变化,这些对象经常面临着剧烈的变化,但是他们却拥有着比较稳定一致的接口。 如何向“客户程序”隔离出“这些易变对象”,从而使得“依赖这些易变对象的客户程序”不随着需求的改变而改变? 三、意图
使用原型实例指定创建对象的种类,然后通过拷贝这些原型来创建新的对象。 四、要点
Prototype模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,它同样要求这些“易变类”拥有“稳定的接口”。
Prototype模式对于“如何创建易变类的实体对象”(创建型模式除了Singleton模式以外,都是用于解决创建易变类的实体对象的问题的)采用“原型克隆”的方法来做,它使得我们可以非常灵活地动态创建“拥有某些稳定接口”的新对象——所需工作仅仅是注册一个新类的对象(即原型),然后在任何需要的地方不断地Clone。
Prototype模式中的Clone方法可以利用.NET中的Object类的MemberwiseClone()方法或者序列化来实现深拷贝。 抽象工厂:
public abstract class NormalActor { public abstract NormalActor Clone() {
return (NormalActor)this.MemberwiseClone(); } }
public abstract class FlyActor { public abstract FlyActor Clone() {
return (FlyActor)this.MemberwiseClone(); } }
public abstract class WaterActor { public abstract WaterActor Clone() {
return (WaterActor)this.MemberwiseClone(); }
}
public class NormalActorB : NormalActor { } public class NormalActorB:NormalActor { } public class FlyActorA:FlyActor { } public class FlyActorB:FlyActor { }
public class WaterActorA:WaterActor { }
游戏系统:
public class GameSystem {
public void Run(NormalActor normalActor, FlyActor flyactor, WaterActor wateractor) {
NormalActor normalActor1 = normalActor.Clone(); NormalActor normalActor2 = normalActor.Clone(); NormalActor normalActor3 = normalActor.Clone(); NormalActor normalActor4 = normalActor.Clone(); NormalActor normalActor5 = normalActor.Clone(); FlyActor flyactor1 = flyactor.Clone(); FlyActor flyactor2 = flyactor.Clone(); WaterActor wateractor1 = wateractor.Clone(); WaterActor wateractor2 = wateractor.Clone(); }
}
将需要new的具体对象用参数传入,这样在GameSystem这个客户程序里面就只依赖于抽象而不依赖于具体了。具体的NormalActorA、FlyActorA等都不出现在GameSystem中。
客户程序:
static void Main(string[] args)
{
GameSystem gameSystem = new GameSystem();
gameSystem.Run(new NormalActorB(), new FlyActorA(), new WaterActorA()); }
有关创建型模式的讨论
Singleton模式解决的是实体对象个数的问题。除了Singleton之外,其他创建型模式解决的都是new所带来的耦合关系。
Factory Method,Abstract Factory,Builder都需要一个额外的工厂类来负责实例化“易变对象”,而Prototype则是通过原型(一个特殊的工厂类)来克隆“易变对象”。(其实原型就是一个特殊的工厂类,它只是把工厂和实体对象耦合在一起了)
如果遇到“易变类”,起初的设计通常从Factory Method开始,当遇到更多的复杂变化时,再考虑重构为其他三种工厂模式(Abstract Factory,Builder,Prototype)。 一般来说,如果可以使用Factory Method,那么一定可以使用Prototype。但是Prototype的使用情况一般是在类比较容易克隆的条件之上,如果是每个类实现比较简单,都可以只用实现MemberwiseClone,没有引用类型的深拷贝,那么就更适合了。Prototype如果要实现深拷贝,还需要在每个要克隆的类上加序列化标签,这点复杂度要考虑进程序中。
Adapter适配器模式(结构型模式)
一、适配:即在不改变原有实现的基础上,将原先不兼容的接口转换为兼容的接口。 二、动机
在软件系统中,由于应用环境的变化,常常需要将“一些现存的对象”放在新的环境中应用,但是新环境要求的接口是这些现存对象所不满足的。
如何应对这种“迁移的变化”?如何既能利用现有对象的良好实现,同时又能满足新的应用环境所要求的接口? 三、意图
将一个类的接口转换成客户希望的另一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。 四、要点
Adapter模式主要应用于“希望复用一些现存的类,但是接口又与复用环境要求不一致的情况”,在遗留代码复用、类库迁移等方面非常有用。
GoF23定义了两种Adapter模式的实现结构:对象适配器和类适配器。但类适配器采用“多继承”的实现方式,带来了不良的高耦合,所以一般不推荐使用。对象适配器采用“对象组合”的方式,更符合松耦合精神。
Adapter模式可以实现的非常灵活,不必拘泥于GoF23中定义的两种结构。例如,完全可以将Adapter模式中的“现存对象”作为新的接口方法参数,来达到适配的目的。
Adapter模式本身要求我们尽可能地使用“面向接口的编程”风格,这样才能在后期很方便地适配。
//对象适配器(常用)
class MyStack : IStack {
ArrayList list; public MyStack() {
list = new ArrayList(); }
public void Push(object item) {
list.Add(item); }
object Pop() {
return list.RemoveAt(list.Count - 1); } }
//这种实际上是一种委派的调用,本来是发送请求给MyStack,但是MyStack实际上是委
派给list去处理。MyStack在这里其实就是Adapter(适配对象),list即是Adaptee(被适配的对象),而IStack就是客户期望的接口。 //类适配器(少用)
Bridge桥接模式(结构型模式)
一、问题的出现
假如我们需要开发一个同时支持PC和手机的坦克游戏,游戏在PC和手机上功能都一样,都有同样的类型,面临同样的功能需求变化,比如坦克可能有很多种不同的型号:T50,T75,T90??
对于其中的坦克设计,我们可能很容易设计出来一个Tank的抽象基类,然后各种不同型号的Tank继承自该类;
另外的变化原因
但是PC和手机上的图形绘制、声效、操作等实现完全不同??因此对于各种型号的坦克,都要提供各种不同平台上的坦克实现:
二、动机
思考上述问题的症结:事实上由于Tank类型的固有逻辑,使得Tank类型具有了两个变化的维度——一个变化的维度为“平台的变化”,一个变化的维度为“型号的变化”。
如何应对这种“多维度的变化”?如何利用面向对象技术来使得Tank类型可以轻松地沿着“平台”和“型号”两个方向变化,而不引入额外的复杂度? 三、意图
将抽象部分与实现部分分离,使它们都可以独立地变化。
桥模式不能只是认为是抽象和实现的分离,它其实并不仅限于此。如下面的例子,两个都是抽象的部分。更确切的理解,应该是将一个事物中多个维度的变化分离。 四、要点
Bridge模式使用“对象间的组合关系”解耦了抽象和实现之间固有的绑定关系,使得抽象(Tank的型号)和实现(不同的平台)可以沿着格子的维度来变化。
所谓抽象和实现沿着各自维度的变化,即“子类化”它们(比如不同的Tank型号子类,和不同的平台子类),得到各个子类之后,便可以任意组合它们,从而获得不同平台上的不同型号。
Bridge模式有时候类似于多继承方案,但是多继承方案往往违背单一职责原则(即一个类只有一个变化的原因),复用性比较差。Bridge模式是比多继承方案更好的解决方法。
public abstract class Tank
{ protected TankPlatformImplementation tankImpl; public Tank(TankPlatformImplementation tankImpl)
百度搜索“77cn”或“免费范文网”即可找到本站免费阅读全部范文。收藏本站方便下次阅读,免费范文网,提供经典小说综合文库设计模式纵横谈学习笔记(2)在线全文阅读。
相关推荐: