设计模式笔记
完整的设计模式笔记,请参考 这儿
一. UML时序图和类图
1. 类之间的关系
六种类的关系:
- 泛化关系(generalization)
- 实现关系(realize)
- 聚合关系(aggregation)
- 组合关系(composition)
- 关联关系(association)
- 依赖关系(dependency)
1.1. 泛化关系
泛化与实现关系同属于类的继承结构。(泛化和实现)继承关系为 is-a的关系;两个对象之间如果可以用 is-a 来表示,就是继承关系:(…是…)
Example:SUV是汽车,汽车和SUV之间时泛化关系。泛化关系表现为继承非抽象类。
1.2. 实现关系
"车"是一个抽象概念,在现实中并无法直接用来定义对象;只有指明具体的子类(汽车还是自行车),才可以用来定义对象.
Example:汽车、自行车与车(抽象类)之间是实现关系,实现关系表现为继承抽象类.
1.3. 聚合关系
聚合关系用于表示实体对象之间的关系,表示整体由部分构成的语义;
Exampl:一个部门由多个员工组成,员工和部门之间是聚合关系
1.4. 组合关系
与聚合关系一样,组合关系同样表示整体由部分构成的语义;但组合关系是一种强依赖的特殊聚合关系,如果整体不存在了,则部分也将不存在;
Example:公司由多个部门组成,,公司不存在了,部门也将不存在了;因此,公司和部门是组合关系;
1.5. 关联关系
它描述不同类的对象之间的结构关系;它是一种静态关系, 通常与运行状态无关,一般由常识(约定俗成)等因素决定的;它一般用来定义对象之间静态的、天然的结构; 所以,关联关系是一种“强关联”的关系;
Example:乘车人和车票之间就是一种关联关系;学生和学校就是一种关联关系;
注:在最终代码中,关联对象通常是以成员变量的形式实现的;
1.6. 依赖关系
它描述一个对象在运行期间会用到另一个对象的关系;与关联关系不同的是,它是一种临时性的关系,通常在运行期间产生,并且随着运行时的变化; 依赖关系也可能发生变化;
Example:A依赖于B,且它们之间是单向依赖。双向依赖是一种非常糟糕的结构,我们总是应该保持单向依赖,杜绝双向依赖的产生;
注:在最终代码中,依赖关系体现为类构造方法及类方法的传入参数,箭头的指向为调用关系;依赖关系除了临时知道对方外,还可“使用”对方的方法和属性;
2. 时序图
时序图(Sequence Diagram)是显示对象之间交互的图,这些对象是按时间顺序排列的。时序图中显示的是参与交互的对象及其对象之间消息交互的顺序。
时序图包括的建模元素主要有:对象(Actor)、生命线(Lifeline)、控制焦点(Focus of control)、消息(Message)等等。
二. 创建型模式
创建型模式对类的实例化过程进行抽象,能够将软件模块中对象的创建和对象的使用分离。为了使模块的结构清晰,外界对于这些对象只需要知道它们共同的接口,而不需要清楚其具体的实现细节。
包含的模式:
- 简单工厂模式(Simple Factory)
- 工厂方法模式(Factory Method)
- 抽象工厂模式(Abstract Factory)
- 建造者模式(Builder)
- 原型模式(Prototype)
- 单例模式(Singleton)
1. 简单工厂模式
1.1. 模式动机
一个软件系统可以提供多个外观不同的按钮(如圆形按钮、矩形按钮、菱形按钮等),这些按钮源于同一个基类,只是不同子类在继承基类后修改部分属性。如果我们希望在使用这些按钮时,不需要知道这些具体按钮类的名字,只需要知道表示该按钮类的一个参数,并提供一个调用方便的方法,把该参数传入方法即可返回一个相应的按钮对象使得其呈现不同外观。
1.2. 模式定义
简单工厂模式,又称为静态工厂模式。在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的类实例通常具有共同父类,只是在子类中作不同的实现,以体现属性的差异。
1.3. 模式结构
简单工厂模式包含如下角色:
- Factory:工厂角色
工厂角色负责实现创建所有实例的内部逻辑–工厂类; - Product:抽象产品角色
抽象产品角色是所创建的所有对象的父类,负责描述所有实例所共有的公共接口–抽象父类; - ConcreteProduct:具体产品角色
具体产品角色是创建的目标,所创建的duixiang都充当这个角色的某个具体类的实例–具体子类。
1.4. 时序图
1.5. 简单工厂模式的优缺点
优点:
- 工厂类含有必要的判断逻辑,可以决定在什么时候创建哪一个产品类的实例,客户端可以免除直接创建产品对象的责任,而只是消费产品;
- 客户端无需知道所创建的具体产品类的类名,更不关心具体的创建细节,只需要知道具体产品类的对应参数;
- 通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类。
缺点:
- 工厂类集中了所有产品的创建逻辑,一旦不能工作,整个系统都要受到影响;
- 使用简单工厂模式会增加系统中类的个数,增加了系统的复杂度–废话;
- 系统扩展困难,一旦添加新产品需要修改工厂类逻辑;
- 简单工厂模式使用静态工厂方法,造成工厂角色无法形成基于继承的等级结构;
1.6. 适用环境
在以下情况可以使用简单工厂模式:
- 工厂类负责创建的对象比较少:由于创建的对象较少,不会造成工厂方法的业务逻辑过于复杂;
- 客户端只知道传入工厂类的参数,对如何创建对象不关心:客户端不需要关心类的创建细节,甚至不需要知道类名,只需要知道类型对应的参数。
1.7. 代码示例
//工厂类
public class Factory {
//静态工厂方法
public static Product createProduct(String proname){
if (proname=="A"){
return new ConcreteProductA();
}else if (proname=="B"){
return new ConcreteProductB();
}
return null;
}
public static void main(String[] arg){
Product productA = Factory.createProduct("A");
productA.use();
}
}
//抽象产品父类
public abstract class Product {
private int type;
public abstract void use();
}
//具体产品类子类
class ConcreteProductA extends Product {
@Override
public void use() {
System.out.println("Create a ProductA");
}
}
//输出结果
Create a ProductA
2. 工厂方法模式
2.1. 模式动机
现在对系统进行修改,不再设计一个按钮工厂类来统一负责所有产品的创建,而是将具体按钮的创建过程交给具体工厂子类完成。我们定义一个抽象的按钮工厂类,再定义具体的工厂子类来生成圆形、矩形、菱形等不同类型的按钮,它们对抽象工厂类中的生产方法进行具体的实现。这种模式结构可以在不修改具体工厂类的情况下引进新的产品,只需要为新类型的按钮创建一个具体的工厂子类来获取新的按钮实例。有利于类的扩展和维护。
2.2. 模式定义
工厂方法模式又称为工厂模式,也叫虚拟构造器(Vertical Constructor)模式或者多态工厂(Polymorphic Factory)模式。在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类负责生成具体的产品对象。这样做的目的是将产品类的实例化操作延迟到工厂子类完成,即通过工厂子类来确定实现哪一个具体产品类。
2.3. 模式结构
工厂方法模式包含下列角色:
- Product:抽象产品
- ConcreteProduct:具体产品
- Factory:抽象工厂
- ConcreteFactory:具体工厂
2.4. 时序图
2.5. 工厂方法模式的优缺点
优点
- 在工厂方法模式中,用户只需要关心所需产品对应的工厂,无须关心创建细节,甚至不需要知道具体产品类类名。
- 基于工厂角色和产品角色的多态性设计是工厂方法模式的关键。它能够使工厂自主确定要创建的具体的产品对象,而如何创建这一对象的细节则完全封装在具体工厂内部。工厂模式之所以被称为多态工厂模式,是因为所有的具体工厂类都具有同一抽象父类。
- 在系统中加入新产品时,只需要添加一个具体工厂和具体产品即可。因此,系统的可扩展性较好,符合“开闭原则”。
缺点
- 在添加新产品时,需要添加新的具体产品类,同时还要提供对应用于创建实例的具体工厂类。系统中类的个数成对增加,这在一定程度上增加了系统的复杂度。
- 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度。
2.6. 适用环境
在以下情况可以使用工厂方法模式:
- 在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂类即可,具体的产品由具体的工厂类创建;
- 一个类通过其子类来指定创建哪个对象:在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。
- 将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中。
2.7. 代码示例
//具体的工厂方法类
public class ConcreteFactory extends Factory {
@Override
public Product factoryMethod() {
return new ConcreteProduct();
}
public static void main(String[] arg){
//具体的工厂方法
Factory factory = new ConcreteFactory();
Product product = factory.factoryMethod();
product.use();
}
}
//具体的产品类
public class ConcreteProduct extends Product {
@Override
public void use() {
System.out.println("Create a Product");
}
}
//抽象工厂类
abstract class Factory {
public abstract Product factoryMethod();
}
//抽象产品类
abstract class Product {
public abstract void use();
}
//执行结果
Create a Product
3. 抽象工厂模式
3.1. 模式动机
- 在工厂方法模式中,具体工厂负责生产具体产品。而且在一般情况下,一个具体工厂只有一个工厂方法或一组重载的生产方法,对应生产一种具体产品。但是有时候我们需要一个工厂提供多个产品对象,而不是单一的产品对象。
- 抽象工厂模式与工厂方法模式最大的区别在于,工厂方法模式针对一个产品等级结构,而抽象工厂模式针对多个产品等级结构。一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建。当一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象时,抽象工厂模式比工厂方法模式更为简单、高效。
引入概念:
- 产品等级结构: 产品等级结构即产品的继承结构。抽象产品和具体产品之间构成一个产品等级结构,抽象产品是父类,具体产品是子类。
- 产品族: 在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构的一组产品。例如,海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构,海尔电冰箱位于电冰箱产品等级结构。
3.2. 模式定义
抽象工厂模式,提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,属于对象创建型模式。
3.3. 模式结构
- AbstractFactory:抽象工厂
- ConcreteFactory:具体工厂
- AbstractProduct:抽象产品
- Product:具体产品
3.4. 时序图
3.5. 抽象工厂模式的优缺点
优点:
- 抽象工厂模式隔离了具体类的生成,使客户不需要知道什么被创建。由于这种隔离,更换一个具体工厂变得容易。所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需改变具体工厂的实例,就可以在一定程度上改变软件系统的行为。另外,应用抽象工厂模式可以实现高内聚低耦合的设计目的。
- 当一个产品族中 的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象,这对一些需要根据当前环境来决定其行为的软件系统来说,是一种非常实用的设计模式。
- 增加新的具体工厂和产品族很方便,无须修改已有系统,符合开闭原则。
缺点:
- 在添加新的产品对象时,难以扩展抽象工厂来生产新种类的产品,这是因为在抽象工厂角色中规定了所有可能被创建的产品集合,要支持新种类的产品就意味着要对该接口进行扩展,而这将涉及到对抽象工厂角色及其所有子类的修改,显然会带来不便。
- 开闭原则的倾斜性(增加新的工厂和产品族容易,增加新的产品等级结构麻烦)。
3.6. 适用环境
在以下情况适合采用抽象工厂模式:
- 一个系统不应当依赖于产品实例被如何创建、组合和表达的细节,这对于所有类型的工厂模式都重要;
- 系统中有多于一个的产品族,而每次只使用其中一个产品族;
- 属于同一产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来;
- 系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。
3.7. 代码示例
//抽象工厂
public abstract class AbstractFactory {
//生产不同产品等级结构的产品对象
public abstract AbstractProductA createProductA();
public abstract AbstractProductB createProductB();
}
//抽象产品--电视机
public abstract class AbstractProductA {
protected static String type;
protected static String name;
public AbstractProductA() {
type = "电视机";
}
public abstract void watch();
}
//抽象产品--电冰箱
public abstract class AbstractProductB {
protected static String type;
protected static String name;
public AbstractProductB() {
type = "电冰箱";
}
public abstract void use();
}
//电视机的具体产品--海尔电视机
public class ProductA1 extends AbstractProductA {
public ProductA1(String nam) {
name = nam;
}
@Override
public void watch() {
System.out.println(type+"-"+name);
}
}
//电冰箱的具体产品--海尔冰箱V2
public class ProductB2 extends AbstractProductB {
public ProductB2(String nam) {
name = nam;
}
@Override
public void use() {
System.out.println(type+"=="+name);
}
//具体工厂--第二代生产线
public class ConcreteFactory2 extends AbstractFactory {
public ConcreteFactory2() {
System.out.println("这是二代生产线");
}
@Override
public AbstractProductA createProductA() {
return new ProductA2("海尔电视 V2");
}
@Override
public AbstractProductB createProductB() {
return new ProductB2("海尔冰箱 V2");
}
//Main入口
public static void main(String[] args){
//第一代电器生产线
AbstractFactory factory1 = new ConcreteFactory1();
AbstractProductA productA1 = factory1.createProductA();
AbstractProductB productB1 = factory1.createProductB();
productA1.watch();
productB1.use();
//第二代电器生产线
AbstractFactory factory2 = new ConcreteFactory2();
AbstractProductA productA2 = factory2.createProductA();
AbstractProductB productB2 = factory2.createProductB();
productA2.watch();
productB2.use();
}
//执行结果
这是一代生产线
电视机-海尔电视机
电冰箱=海尔电冰箱
这是二代生产线
电视机--海尔电视 V2
电冰箱==海尔冰箱 V2