学习资料:
1、《设计模式:可服用面向对象软件的基础》
2、《敏捷软件开发:原则、模式与实践》
3、《重构:改善既有代码的设计》
4、《Refactoring to Patterns》
5、《面向对象分析与设计》
场景
设计一个人事管理系统,其中一个功能是对不同类型的员工,计算当月的工资——不同类型的员工,拥有不同的薪金计算制度。
结构化设计
(1)获得人事系统中所有可能员工类型;
(2)根据不同的员工类型所对应的不同薪金制度,计算其工资。
面向对象设计
(1)根据不同的员工类型设计不同的类,并使这些类继承自一个Employee抽象类,其中有一个抽象方法GetSalary;
(2)在不同的员工类中,根据自己的薪金制度,重写GetSalary方法。
需求改变……
场景
随着客户公司业务规模的拓展,又出现了更多类型的员工
结构化设计
几乎所有涉及到员工类型的地方都需要做改变
面向对象设计
只需要在新的文件里添加新的员工类,让其继承自Employee抽象类,并重写GetSarary()方法,然后在EmployeeFactory.GetEmployee方法中根据相关条件,产生新的员工类型
面向对象设计三大原则:
1、针对接口编程,而不是针对实现编程;
2、优先使用对象组合(has a),而不是类继承(is a);
3、封装变化点;
使用重构得到模式——设计模式的应用不宜先入为主
五条更具体的设计原则:
1、单一职责原则(SRP)
— 一个类应该仅有一个引起它变化的原因
2、开放封闭原则(OCP)
— 类模块应该是可扩展的,但是不可修改(对扩展开放,对更改封闭)
3、Liskov替换原则(LSP)
— 子类必须能够替换它们的基类
4、依赖倒置原则(DIP)
— 高层模块不应该依赖于底层模块,二者都应该依赖于抽象
— 抽象不应该依赖于实现细节,实现细节应该依赖于抽象
5、接口隔离原则(ISP)
— 不应该强迫客户程序依赖于它们不用的方法
模式分类:
1、从目的看
— 创建型(Creational)模式:负责对象创建
— 结构型(Structural)模式:处理类与对象间的组合
— 行为性(Behavionral)模式:类与对象交互中的职责分配
2、从范围来看
— 类模式处理类与子类的静态关系
— 对象模式处理对象间的动态关系
Singleton单件(创建型模式)
1、存在的问题
有一些特殊的类,必须保证它们在系统中只存在一个实例,才能确保它们的逻辑正确性以及良好的效率——类设计者的责任,而不是使用者的责任。
2、解决思路
— 封装变化点:哪里变化,封装哪里
— 潜台词:如果没有变化,当然不需要额外的封装
3、意图
保证一个类仅有一个实例,并提供一个该实例的全局访问点。
单线程Singleton模式的几个要点:
1、Singleton模式中的实例构造器可以设置为protected以允许子类派生;
2、Singleton模式一般不要支持IClonable接口;
3、Singleton模式一般不要支持序列化;
4、Singleton模式只考虑了对象创建的管理,没有考虑对象销毁的管理;
5、Singleton模式不能用于多线程环境。
单线程Singleton模式扩展:
1、将一个实例扩展到具体的n个实例,例如对象池的实现(如Type);
2、将new构造器的调用转移到其他类中,例如多个类协同工作环境中,某个局部环境只需要拥有某个类的实例(如HttpContext);
3、理解和扩展Singleton模式的核心是“如何控制用户使用new对一个类的实例构造器的任意调用”。
Abstract Factory抽象工厂(创建型模式)
new的问题
1、常规的对象创建方法:Road road = new Road();
2、存在的问题
— 实现依赖,不能应对“具体实例化类型”的变化
3、解决思路
— 封装变化点:哪里变化,封装哪里
— 潜台词:如果没有变化,当然不需要额外的封装
工厂模式的缘起
1、变化点在“对象创建”,因此就封装“对象创建”;
2、面向接口编程—依赖接口而非依赖实现
3、简单工厂示例:
(1)单一对象
class RoadFactory //--------------类库
{
public static Road CreateRoad()
{
return new Road();
}
}
Road road = RoadFactory.CreateRoad(); //在其他地方实现——————客户程序
(2)多个相互依赖的对象
class RoadFactory //--------------类库,无法实现不同风格(变化点)
{
public static Road CreateRoad()
{
return new Road();
}
public static Building CreateBuilding()
{
return new Building();
}
public static Tunnel CreateTunnel()
{
return new Tunnel();
}
public static Jungle CreateJungle()
{
return new Jungle();
}
............ //创建其他对象
}
//以下代码在其他地方实现——————客户程序(相对稳定)
Road road = RoadFactory.CreateRoad();
Building building = RoadFactory.CreateBuilding();
............... //对象交互的代码
抽象工厂
1、存在的问题
— 不能应对“不同系列对象”的变化(比如不同风格的对象)
2、解决思路
— 使用面向对象的技术来“封装”变化点
— 绕过常规的对象创建方法,避免客户程序和“多系列具体对象创建”的紧耦合
— 提供一个接口,让该接口负责“多系列具体对象创建”而不用指定具体的类
3、意图
— 实现“多种相互依赖的对象”的创建工作
— 实现“多系列对象”的创建工作
4、应用要点
(1)如果没有“多系列对象的构建”的需求变化,则没有必要使用Abstract Factory模式;
(2)“系列对象”指的是这些对象之间有相互依赖、或作用的关系;
(3)Abstract Factory模式主要在于应对“新系列”的需求变化,而难以应对“新对象”的需求变化;
(4)Abstract Factory模式经常和Factory Method模式共同组合来应对“新对象”的需求变化。
Builder 生成器(创建型模式)
Builder模式的缘起
1、假设创建游戏中的一个房屋设施(House),该房屋的构建由几个部分构成,且各个部分要富于变化;
2、如果使用最直观的设计方法,每一个房屋部分的变化,都将导致房屋构建的重新修正。
动机
1、在面临一个复杂对象的创建工作时,通常由各部分的子对象用一定的算法构成,由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法相对稳定;
2、提供一种“封装机制”来隔离出复杂对象的各个部分的变化,从而保持系统中的稳定构建算法不随着需求改变而改变;
意图
将一个复杂对象的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
3、应用要点
(1)Builder模式主要用于“分步骤构建一个复杂的对象”
— 其中“分步骤”是一个稳定的算法,而复杂对象的各个部分则经常变化
(2)变化点在哪里,封装哪里
— Builder模式用于应对“复杂对象各个部分”的需求变动,而难以应对“分步骤构建算法”的需求变动
(3)Abstract Factory模式解决“系列对象”的需求变化,Builder模式解决“对象部分”的需求变化
— Builder模式通常和Composite模式组合使用
Factory Method 工厂方法(创建型模式)
从耦合关系谈起:耦合关系直接决定着软件面对变化时的行为
— 模块与模块之间的紧耦合使得软件面对变化时,相关的模块都要随之更改
— 模块与模块之间的松耦合使得软件面对变化时,一些模块更容易被替换或者更改,但其他模块保持不变
动机
— 在软件系统中,经常面临着“某个对象”的创建工作,由于需求的变化使得该对象的具体实现经常面临着剧烈的变化,但是它却拥有比较稳定的接口
— 提供一种“封装机制”来隔离出“易变对象”的变化,从而保持系统中“其他依赖该对象的对象”不随着需求改变而改变
意图
— 定义一个用于创建对象的接口,让子类决定实例化哪一个类,从而使得一个类的实例化延迟到子类
应用要点
(1)Factory Method模式主要用于隔离类对象的使用者和具体类型之间的耦合关系。
— 面对一个经常变化的具体类型,紧耦合关系会导致软件的脆弱
(2)Factory Method模式通过面向对象的手法将所要创建的具体对象延迟到子类,从而实现一种扩展的策略,较好地解决了这种紧耦合关系。
(3)Factory Method模式解决“单个对象”的需求变化;Abstract Factor模式解决“系列对象”的需求变化;Builder模式解决“对象部分”的需求变化。
Prototype原型模式(创建型模式)
依赖关系的倒置
— 抽象不应该依赖于实现细节,实现细节依赖于抽象
动机
— 在软件系统中,经常面临着“某些结构复杂的对象”的创建工作,由于需求的变化使得该对象的具体实现经常面临着剧烈的变化,但是它却拥有比较稳定一致的接口
— 提供一种“封装机制”向客户程序隔离出“易变对象”,从而保持系统中“其他依赖这些易变对象的对象的客户程序”不随着需求改变而改变
意图
— 使用原型实例指定创建对象的种类,然后通过拷贝这些原型来创建新的对象
应用要点
(1)Prototype原型模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,它同样要求这些“易变类”拥有“稳定的接口”。
(2)Prototype原型模式采用“原型克隆”的方式创建易变类的实体对象,从而实现灵活地动态创建“拥有某些稳定接口”的新对象。
(3)Prototype原型模式中的Clone方法可以利用.NET中的Object类的MemberwiseClone()方法或者序列化方法来实现深拷贝。
有关创建型模式的讨论
1、Singleton模式解决的是实体对象个数的问题,其他创建型模式解决的都是new所带来的耦合关系;
2、Factory Method、Abstract Factory、Builder都需要一个额外的工厂类来负责实例化“易变对象”, 而Prototype则是通过原型(一个特殊的工厂类)来克隆“易变对象”;
3、如果遇到“易变对象”,起初的设计通常从Factory Method开始,当遇到更多的复杂变化时,再考虑重构为其他三种工厂模式(Abstract Factory、Builder、Prototype)。