7.2 设计模式

7.3.1 设计模式的要素

“每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心。这样,你就能一次又一次地使用该方案而不必做重复劳动”。设计模式的核心在于提供了相关问题的解决方案,使得人们可以更加简单方便地复用成功的设计和体系结构。

设计模式一般有以下4个基本要素。
(1)模式名称(Pattemm Name)。一个助记名,它用一两个词来描述模式的问题、解决方案和效果。命名一个新的模式增加了设计词汇。设计模式允许在较高的抽象层次上进行设计。基于一个模式词汇表,就可以讨论模式并在编写文档时使用它们。模式名可以帮助人们思考,便于人们与其他人交流设计思想及设计结果。找到恰当的模式名也是设计模式工作的难点之一。

(2)问题(Problem)。问题描述了应该在何时使用模式。它解释了设计问题和问题存在的前因后果,可能描述了特定的设计问题,如怎样用对象表示算法等;也可能描述了导致不灵活设计的类或对象结构。有时候,问题部分会包括使用模式必须满足的一系列先决条件。(3)解决方案(Solution)。解决方案描述了设计的组成成分、它们之间的相互关系及各自的职责和协作方式。因为模式就像一个模板,可应用于多种不同场合。所以解决方案并不描述一个特定的具体的设计或实现,而是提供设计问题的抽象描述和怎样用一个具有一般意义的元素组合(类或对象组合)来解决这个问题。

(4)效果(Consequences)。效果描述了模式应用的效果及使用模式应权衡的问题。尽管描述设计决策时,并不总提到模式效果,但它们对于评价设计选择和理解使用模式的代价及好处具有重要意义。软件效果大多关注对时间和空间的衡量,它们也表述了语言和实现问题。因为复用是面向对象设计的要素之一,所以模式效果包括它对系统的灵活性、扩充性或可移植性的影响,显式地列出这些效果对理解和评价这些模式很有帮助。
设计模式确定了所包含的类和实例,它们的角色、协作方式以及职责分配。每一个设计模式都集中于一个特定的面向对象设计问题或设计要点,描述了什么时候使用它,在另一些设计约束条件下是否还能使用,以及使用的效果和如何取舍。

按照设计模式的目的可以分为三大类,如表 7-1 所示。

创建型模式与对象的创建有关;结构型模式处理类或对象的组合;行为型模式对类或对象怎样交互和怎样分配职责进行描述。在这里插入图片描述

7.3.2 创建型设计模式

1.Abstract Factory(抽象工厂)

1)意图

提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们

2)结构
抽象工厂模式的结构如图 7-25 所示,
在这里插入图片描述
其中:

  • AbstractFactory 声明一个创建抽象产品对象的操作接口。

  • ConcreteFactory 实现创建具体产品对象的操作。

  • AbstactProduct 为一类产品对象声明一个接口。

  • ConcreteProduct 定义一个将被相应的具体工厂创建的产品对象,实现 AbstractProduct
    接口。

  • Client 仅使用由 AbstractFactory 和 AbstractProduct 类声明的接囗。

3)适用性
Abstract Factory 模式适用于:

  • 一个系统要独立于它的产品的创建、组合和表示时。
  • 一个系统要由多个产品系列中的一个来配置时。
  • 当要强调一系列相关的产品对象的设计以便进行联合使用时。
  • 当提供一个产品类库,只想显示它们的接口而不是实现时。
  1. 应用举例:多数据源支持
    在这里插入图片描述
    根据图6-1的类图,我们可以看到有以下几个类:
  1. IFactory:抽象工厂接口。
  2. SqlserverFactory:SQL Server数据库工厂实现。
  3. AccessFactory:Access数据库工厂实现。
  4. IDepartment:部门接口。
  5. SqlserverDepartment:SQL Server数据库中的部门表实现。
  6. AccessDepartment:Access数据库中的部门表实现。

以下是一个简单的Java代码示例,展示了如何使用这些类来创建不同的数据库连接和操作:

class Department{
	private int id;
    private String name;
	/*代码省略*/
}

IDepartment.java

// IDepartment.java
public interface IDepartment {
    public void insert(Departent departent);
    public Department getDepartment(int id);
}

SqlserverDepartment.java


// SqlserverDepartment.java
public class SqlserverDepartment implements IDepartment {
    
    @Override
    public void insert(Department departent) {
        System.out.println("Insert a record into Department in sQL Server!");
    }
    
	public Department getDepartment(int id){
		// 假设这里从数据库获取到了一个部门信息
        return new Department(id, "Sales");
	}
}

AccessDepartment.java

// AccessDepartment.java
public class AccessDepartment implements IDepartment {
 	
 	@Override
    public void insert(Department department) {
        System.out.println("Insert a record into Department in ACCEss!");
    }
    
	public Department getDepartment(int id){
		// 假设这里从数据库获取到了一个部门信息
        return new Department(id, "HR");
	}
   
}

IFactory.java


// IFactory.java
public interface IFactory {
    IDepartment createDepartment();
}

SqlserverFactory.java

// SqlserverFactory.java
public class SqlserverFactory implements IFactory {
    @Override
    public IDepartment createDepartment() {
        return new SqlserverDepartment();
    }
}

AccessFactory.java

// AccessFactory.java
public class AccessFactory implements IFactory {
    @Override
    public IDepartment createDepartment() {
        return new AccessDepartment();
    }
}

Client.java

// Client.java
public class Client {
    public static void main(String[] args) {
        IFactory factory;
        Department department = new Department(1, "Engineering");

        // 使用SQL Server数据库
        factory = new SqlserverFactory();
        IDepartment sqlServerDepartment = factory.createDepartment();
        sqlServerDepartment.insert(department);

        // 使用Access数据库
        factory = new AccessFactory();
        IDepartment accessDepartment = factory.createDepartment();
        accessDepartment.insert(department);

        // 获取部门信息
        Department retrievedDepartment = sqlServerDepartment.getDepartment(1);
        System.out.println("Retrieved Department from SQL Server: " + retrievedDepartment.getName());

        retrievedDepartment = accessDepartment.getDepartment(1);
        System.out.println("Retrieved Department from Access: " + retrievedDepartment.getName());
    }
}

在这个例子中,我们首先定义了一个抽象工厂接口IFactory,然后实现了两个具体的工厂类SqlserverFactoryAccessFactory。每个工厂类都有一个方法createDepartment(),用于创建相应的数据库表对象。

接着,我们定义了部门接口IDepartment,并实现了两个具体类SqlserverDepartmentAccessDepartment,分别对应SQL Server和Access数据库的操作。

最后,在客户端Client类中,我们通过工厂模式创建了不同类型的数据库表对象,并调用它们的方法进行数据库操作。这样,我们就能够轻松地切换和扩展支持的数据库类型。

2.Builder(生成器)

1)意图

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

2)结构生成器模式的结构如图 7-26 所示。
在这里插入图片描述
其中:

  • Builder 为创建一个 Product 对象的各个部件指定抽象接口。
  • ConcreteBuilder 实现 Builder 的接口以构造和装配该产品的各个部件,定义并明确它所创建的表示,提供一个检索产品的接口。
  • Director 构造一个使用 Builder 接口的对象。
  • Product 表示被构造的复杂对象。ConcreteBuilder 创建该产品的内部表示并定义它的装配过程。包含定义组成组件的类,包括将这些组件装配成最终产品的接口。

3)适用性
Builder 模式适用于:

  • 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
  • 当构造过程必须允许被构造的对象有不同的表示时。

4)应用举例

示例:餐厅套餐 软考 2017上 科目二 试题六
在这里插入图片描述
在这个例子中:

  1. Pizza:表示披萨产品,包含零件(parts)属性。
  2. PizzaBuilder:抽象建造者类,声明了创建新披萨和构建披萨零件的方法。
  3. HawaiianPizzaBuilderSpicyPizzaBuilder:具体建造者类,分别实现了构建夏威夷风味和辣味披萨的方法。
  4. Waiter:指挥者类,负责协调建造者和产品的关系。

以下是对代码的详细解释:

Pizza.java

public class Pizza {
    private String parts;

    public void setParts(String parts) {
        this.parts = parts;
    }

    @Override
    public String toString() {
        return "Pizza{" +
                "parts='" + parts + '\'' +
                '}';
    }
}
  • 定义了一个简单的披萨类,包含零件属性和对应的getter/setter方法以及toString方法。

PizzaBuilder.java

public abstract class PizzaBuilder {
    protected Pizza pizza;

    public Pizza getPizza() {
        return pizza;
    }

    public void createNewPizza() {
        pizza = new Pizza();
    }

    public abstract void buildParts();
}
  • 抽象建造者类,声明了创建新披萨和构建披萨零件的方法。

HawaiianPizzaBuilder.java

public class HawaiianPizzaBuilder extends PizzaBuilder {
    @Override
    public void buildParts() {
        pizza.setParts("cross + mild + ham&pineapple");
    }
}
  • 具体建造者类,实现了构建夏威夷风味披萨的方法。

SpicyPizzaBuilder.java

public class SpicyPizzaBuilder extends PizzaBuilder {
    @Override
    public void buildParts() {
        pizza.setParts("panbaked + hot + pepperoni&salamis");
    }
}
  • 具体建造者类,实现了构建辣味披萨的方法。

Waiter.java

public class Waiter {
    private PizzaBuilder pizzaBuilder;

    public void setPizzaBuilder(PizzaBuilder pizzaBuilder) {
        this.pizzaBuilder = pizzaBuilder;
    }

    public Pizza getPizza() {
        return pizzaBuilder.getPizza();
    }

    public void construct() {
        pizzaBuilder.createNewPizza();
        pizzaBuilder.buildParts();
    }
}
  • 指挥者类,负责协调建造者和产品的关系。

FastFoodOrdering.java

public class FastFoodOrdering {
    public static void main(String[] args) {
        Waiter waiter = new Waiter();

        HawaiianPizzaBuilder hawaiian_pizzabuilder = new HawaiianPizzaBuilder();
        waiter.setPizzaBuilder(hawaiian_pizzabuilder);
        waiter.construct();
        System.out.println(waiter.getPizza());

        SpicyPizzaBuilder spicy_pizzabuilder = new SpicyPizzaBuilder();
        waiter.setPizzaBuilder(spicy_pizzabuilder);
        waiter.construct();
        System.out.println(waiter.getPizza());
    }
}
  • 主程序,演示如何使用建造者模式创建不同口味的披萨。

输出结果为:

pizza: Pizza{parts='cross + mild + ham&pineapple'}
pizza: Pizza{parts='panbaked + hot + pepperoni&salamis'}

3.Factory Method(工厂方法)

1)意图

定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method 使一个类的实例化延迟到其子类。

2)结构
工厂方法模式的结构如图 7-27 所示。
在这里插入图片描述
其中:

  • Product 定义工厂方法所创建的对象的接口。
  • ConcreteProduct 实现 Product 接。
  • Creator 声明工厂方法,该方法返回一个 Product 类型的对象。Creator 也可以定义一个工厂方法的默认实现,它返回一个默认的 ConcreteProduct 对象,可以调用工厂方法以创建一个 Product 对象。
  • ConcreteCreator 重定义工厂方法以返回一个ConcreteProduct 实例。

3)适用性
Factory Method 模式适用于:

  • 当一个类不知道它所必须创建的对象的类的时候。
  • 当一个类希望由它的子类来指定它所创建的对象的时候。
  • 当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。

4.Prototype(原型)

1)意图

用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。

2)结构
原型模式的结构如图 7-28 所示。
在这里插入图片描述

其中:

  • Prototype 声明一个复制自身的接口。
  • ConcretePrototype 实现一个复制自身的操作。
  • Client 让一个原型复制自身从而创建一个新的对象。

3)适用性
Prototype 模式适用于:

  • 当一个系统应该独立于它的产品创建、构成和表示时。
  • 当要实例化的类是在运行时刻指定时,例如,通过动态装载。
  • 为了避免创建一个与产品类层次平行的工厂类层次时。
  • 当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们,可能比每次用合适的状态手工实例化该类更方便一些。
  1. 应用举例
    示例:软考2013上 科目二 试题六
    在这里插入图片描述
  1. 接口 Cloneable:定义了一个泛型方法 clone(),用于创建对象的克隆。
  2. WorkExperience:表示一份工作经验。实现了 Cloneable<WorkExperience> 接口,并提供了深复制的方法。
  3. Resume:表示一份简历。使用了 ArrayList<WorkExperience> 来存储多份工作经验。同样实现了 Cloneable<Resume> 接口,并在 clone() 方法中进行了浅复制和深复制操作。
  4. 主函数 main:演示如何创建简历实例,设置个人信息和工作经验,然后通过调用 clone() 方法生成简历副本,并对副本进行修改。

这个实现考虑到了简历可能包含多条工作经验的情况,并且通过深复制确保每份简历都是独立的,不会因为修改某份简历而影响到其他简历。


interface Cloneable  {
    Object clone();
}
class WorkExperience implements Cloneable { // 工作经历
    private String workDate;
    private String company;
    
    public WorkExperience(String workDate, String company) {
        this.workDate = workDate;
        this.company = company;
    }
    
    @Override
    public Object clone() {
        WorkExperience obj = new WorkExperience(); //(2);
		obj.workDate= this.workDate;
		obj.company= this.company;
		return obj;
    }
    
    // 其余方法(如 getters 和 setters)可以在这里添加
}
class Resume implements Cloneable { // 简历
    private String name;
    private String sex;
    private String age;
    private WorkExperience works ;
    
    public Resume(String name) {
        this.name = name;
        work = new WorkExperience()
    }
    
    public void setPersonInfo(String sex, String age) {
        this.sex = sex;
        this.age = age;
    }
    
    public void setWorkExperience(String workDate, String company) {
       /*代码略*/
    }
    
    @Override
    public Object clone() throws CloneNotSupportedException {
        Resume obj = (Resume) a.clone(); // 浅复制
        //其余代码省略 // 深复制工作经验列表
        return obj;
    }
    
    // 其余方法(如 getters 和 setters)可以在这里添加
}
public class WorkResume {
    public static void main(String[] args) {
        Resume a = new Resume("张三");
        a.setPersonInfo("男", "29");
        a.SetWorkExperience("1998~2000""XXX公司");
         
        Resume b = (Resume) a.clone(); // 创建简历副本
        b.SetWorkExperience("2001~2006","YYY 公司”);
    }
}

5.Singleton(单例)

1)意图

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

2)结构
单例模式的结构如图 7-29 所示。
在这里插入图片描述
其中:
Singleton 指定一个 Instance 操作,允许客户访问它的唯一实例, Instance 是一个类操作;可能负责创建它自己的唯一实例。

3)适用性
Singleton 模式适用于:

  • 当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。
  • 当这个唯一实例应该是通过子类化可扩展的,并且客户无须更改代码就能使用一个扩展的实例时。

6.创建型模式比较

用于系统创建的那些对象的类对系统进行参数化有两种常用方法:生成创建对象的类的子类和对系统进行参数化的方法。前者对应于使用Factory Method 模式,其主要缺点是仅为了改变产品类就可能需要创建一个新的子类。这种改变可能级联发生,例如,如果产品的创建者本身是一个工厂方法创建的,那么也必须重定义它的创建者。后者更多地依赖于对象的复合,定义一个对象负责明确产品对象的类,并将它作为该系统的参数。这是Abstract Factory、Builder和 Prototype 模式的关键特征,都涉及创建一个新的负责创建产品对象的“工厂对象”。AbstractFactory 由这个工厂对象产生多个对象。Builder 由这个工厂对象使用一个相对复杂的协议,逐步创建一个复杂产品。Prototype 由该工厂对象通过复制原型对象来创建产品对象。在这种情况下,由于原型负责返回产品对象,所以工厂对象和原型是同一个对象。

7.3.3 结构性设计模式

结构型设计模式涉及如何组合类和对象以获得更大的结构。结构型类模式采用继承机制来组合接口或实现。一个简单的例子是采用多重继承方法将两个以上的类组合成一个类,结果这个类包含了所有父类的性质。这一模式尤其有助于多个独立开发的类库协同工作。其中一个例子是类形式的 Adapter 模式。一般来说,适配器使得一个接口与其他接口兼容,从而给出了多个不同接口的统一抽象。为此,类适配器对一个 adaptee 类进行私有继承。这样,适配器就可
以用 adaptee 的接口表示它的接口。

结构型对象模式不是对接口和实现进行组合,而是描述了如何对一些对象进行组合,从而实现新功能的一些方法。因为可以在运行时刻改变对象组合关系,所以对象组合方式具有更大的灵活性,而这种机制用静态类组合是不可能实现的。

Composite 模式是结构型对象模式的一个实例。它描述了如何构造一个类层次式结构,这一结构由两种类型的对象所对应的类构成。其中的组合对象使得用户可以组合基元对象以及其他的组合对象,从而形成任意复杂的结构。在Proxy 模式中,proxy 对象作为其他对象的一个方便的替代或占位符。它的使用可以有多种形式,例如可以在局部空间中代表一个远程地址空间中的对象,也可以表示一个要求被加载的较大的对象,还可以用来保护对敏感对象的访问。Proxy 模式还提供了对对象的一些特有性质的一定程度上的间接访问,从而可以限制、增强或修改这些性质。Flyweight模式为了共享对象定义了一个结构。至少有两个原因要求对象共享:效率和一致性。Flyweight 的对象共享机制主要强调对象的空间效率。使用很多对象的应用必须考虑每一个对象的开销。使用对象共享而不是进行对象复制,可以节省大量的空间资源。但是,仅当这些对象没有定义与上下文相关的状态时,它们才可以被共享。Flweight的对象没有这样的状态。任何执行任务时需要的其他一些信息仅当需要时才传递过去。由于不存在与上下文相关的状态,因此 Flyweight 对象可以被自由地共享。

如果说 Flyweight 模式说明了如何生成很多较小的对象,那么 Facade 模式则描述了如何用单个对象表示整个子系统。模式中的facade 用来表示一组对象,facade 的职责是将消息转发给它所表示的对象。Bridge 模式将对象的抽象和其实现分离,从而可以独立地改变它们。Decorator 模式描述了如何动态地为对象添加职责。Decorator 模式是一种结构型模式,这一模式采用递归方式组合对象,从而允许添加任意多的对象职责。例如,一个包含用户界面组件的 Decorator 对象可以将边框或阴影这样的装饰添加到该组件中,或者它可以将窗口滚动和缩放这样的功能添加到组件中。可以将一个

Decorator 对象嵌套在另外一个对象中,就可以很简单地增加两个装饰,添加其他的装饰也是如此。因此,每个Decorator 对象必须与其组件的接口兼容并且保证将消息传递给它。Decorator 模式在转发一条信息之前或之后都可以完成它的工作(例如绘制组件的边框)。许多结构型模式在某种程度上具有相关性。

1. Adapter (适配器)

Adapter (适配器) 意图、结构、适用性 以及应用举例

2. Bridge(桥接)

Bridge (桥接) 意图、结构、适用性 以及应用举例

3.Composite(组合)

Composite(组合) 意图、结构、适用性 以及应用举例

4.Decorator(装饰)

Decorator(装饰) 意图、结构、适用性 以及应用举例

5.Facade(外观)

Facade(外观) 意图、结构、适用性 以及应用举例

6.Flyweight(享元)

Flyweight(享元) 意图、结构、适用性 以及应用举例

7.Proxy(代理)

Proxy(代理) 意图、结构、适用性 以及应用举例

8. 结构型模式比较

Adapter 模式和 Bridge 模式具有一些功能特征,都给另一个对象提供了一定程度上的间接性,因而有利于系统的灵活性,另外都涉及从自身以外的一个接口向这个对象转发请求。Adapte1模式主要是为解决两个已有接口之间不匹配的问题,不考虑这些接口是怎样实现的,也不考虑它们各自可能会如何演化。这种方式不需要对两个独立设计的类中的任何一个进行重新设计,就能够使它们协同工作。Bridge 模式则对抽象接口与它的(可能是多个)实现部分进行桥接。虽然这一模式运行使用者修改实现它的类,但是它仍然为用户提供了一个稳定的接口,也会在系统演化时适应新的实现。Adapter 模式和 Bridge 模式通常被用于软件生命周期的不同阶段,针对不同的问题。Adapter 模式在类已经设计好后实施;而 Bridge模式在设计类之前实施。

Composite 模式和 Decorator 模式具有类似的结构,说明它们都是基于递归组合来组织可变数目的对象。Decorator 旨在能够不需要生成子类即可给对象添加职责,这避免了静态实现所有功能组合而导致子类急剧增加。Composite旨在构造类,使多个相关的对象能够以统一的方式处理,而多重对象可以被当作一个对象来处理,重点在于表示。两者通常协同使用。

Decorator 模式和 Proxy 模式都描述了怎样为对象提供一定程度上的间接引用。Proxy 模式
构成一个对象并为用户提供一致的接口,与 Decorator 模式不同的是,Proxy 模式不能动态地添加或分离性质,也不是为递归组合而设计的,它强调一种关系(Proxy 与它的实体之间的关系),这种关系可以静态地表达。其目的是,当直接访问一个实体不方便或不符合需要时,为这个实体提供一个替代者,例如,实体在远程设备上,访问受到限制或者实体是持久存储的。在 Proxy模式中,实体定义了关键功能,而 Proxy提供(或拒绝)对它的访问。在Decorator 模式中,组件仅提供了部分功能,而一个或多个 Decorator 负责完成其他功能。Decorator 模式适用于编译时不能(至少不方便)确定对象的全部功能的情况。

7.3.4 行为型设计模式

行为模式涉及算法和对象间职责的分配。行为模式不仅描述对象或类的模式,还描述它们之间的通信模式。这些模式刻画了在运行时难以跟踪的、复杂的控制流。它们将用户的注意力
从控制流转移到对象间的联系方式上来。

行为类模式使用继承机制在类间分派行为。本小节包括两个这样的模式。其中Template Method 较为简单和常用。模板方法是一个算法的抽象定义,它逐步地定义该算法,每一步调用一个抽象操作或一个原语操作,子类定义抽象操作以具体实现该算法。另一种行为类模式是Imtenpreter,它将一个文法表示为一个类层次,并实现一个解释器作为这些类的实例上的一个操作。

行为对象模式使用对象复合而不是继承。一些行为对象模式描述了一组对等的对象怎样相互协作以完成其中任一个对象都无法单独完成的任务。这里一个重要的问题是对等的对象。

如何互相了解对方。对等对象可以保持显式的对对方的引用,但那会增加它们的合度。在极端情况下,每一个对象都要了解所有其他的对象。Mediator 在对等对象间引入一个 mediator
对象以避免这种情况的出现。mediator 提供了松耦合所需的间接性。

Chain ofResponsibility 提供更松的耦合。它让用户通过一条候选对象链隐式地向一个对象发送请求。根据运行时刻情况任一候选者都可以响应相应的请求。候选者的数目是任意的,可以在运行时刻决定哪些候选者参与到链中。

Observer 模式定义并保持对象间的依赖关系。典型的 Observer 的例子是 Smalltalk 中的模型/视图/控制器,其中,一旦模型的状态发生变化,型的所有视图都会得到通知。

其他的行为对象模式常将行为封装在一个对象中并将请求指派给它。Stategy 模式将算法封装在对象中,这样可以方便地指定和改变一个对象所使用的算法。Command 模式将请求封装在对象中,这样它就可作为参数来传递,也可以被存储在历史列表中,或者以其他方式使用。State 模式封装一个对象的状态,使得这个对象的状态对象变化时,该对象可改变它的行为。Visitor 封装分布于多个类之间的行为,而 Iterator 抽象了访问和遍历一个集合中的对象的方式。

1 Chain of Responsibility 责任链模式

Chain of Responsibility (责任链模式) 意图、结构、适用性 以及应用举例

2 Command (命令模式)

Command (命令模式) 意图、结构、适用性 以及应用举例

3 Interpreter (解释器模式)

Interpreter (解释器模式) 意图、结构、适用性 以及应用举例

4 Iterator (迭代器模式)

Iterator (迭代器模式) 意图、结构、适用性 以及应用举例

5 Mediator (中介者模式)

Mediator (中介者模式) 意图、结构、适用性 以及应用举例

6 Memento (备忘录模式)

Memento (备忘录模式)意图、结构、适用性 以及应用举例

7 Observer (观察者模式)

Observer (观察者模式)意图、结构、适用性 以及应用举例

8 State (状态)

State (状态)意图、结构、适用性 以及应用举例

9 Strategy (策略)

Strategy (策略)意图、结构、适用性 以及应用举例

10 Template Method(模板方法)

Template Method (模板方法)意图、结构、适用性 以及应用举例

11 Visitor (访问者模式)

Visitor(访问者模式)意图、结构、适用性 以及应用举例

12 行为模式比较

很多行为模式注重封装变化。当一个程序的某个方面的特征经常发生改变时,这些模式就定义一个封装这个方面的对象。这样,当该程序的其他部分依赖于这个方面时,它们都可以与此对象协作。这些模式通常定义一个抽象类来描述这些封装变化的对象,并且通常该模式依据这个对象来命名。例如:

  • 一个 Strategy 对象封装一个算法。
  • 一个 State 对象封装一个与状态相关的行为。
  • 一个 Mediator 对象封装对象间的协议。
  • 一个 Iterator 对象封装访问和遍历一个聚集对象中的各个组件的方法。

这些模式描述了程序中很可能会改变的方面。大多数模式有两种对象:封装该方面特征的新对象和使用这些新对象的已有对象。但并非所有的对象行为模式都有这样的分割功能,例如。Chain ofResponsibility 可以处理任意数目的对象(即一个链),而所有这些对象可能已经存在于系统中了。这也说明了行为模式的另一个不同点:并非所有的行为式都定义类之间的静态通信关系。

一些模式引入总是被用作参数的对象。例如 Visitor,一个 Visitor 对象是一个多态的 Accep操作的参数,这个操作作用于该 Visitor 对象访问的对象。其他模式定义一些可作为令牌进行传递的对象,这些对象将在稍后被调用。例如,Command和Memento。在 Command 中,令牌代表一个请求;在 Memento 中,两台代表在一个对象在某个特定时刻的内部状态。在这两种情况下,令牌都可以有一个复杂的内部表示,但客户并不会意识到这一点。另外,在Command 模式中多态特别重要,这是因为执行 Command 对象是一个多态操作。而 Memento接口非常小,以至于备忘录只能作为一个值传递,因此,它很可能根本不给它的客户提供任何多态操作。

Mediator 和 Observer 是相互竞争的模式,它们之间的差别是:Observer 通过引入 Observer和 Subject 对象米分布通信,而 Mediator 对象则封装了其他对象间的通信。Observer 模式中不存在封装一个约束的单个对象,而必须是 Observer 和 Subiect 对象相互协作来维护这个约束。通信模式由 Observer 和 Subject 连接的方式决定:一个 Subject通常有多个 Observer,并且有时一个 Subject 的 Observer 也是另一个 Observer 的日标。Observer 模式有利于 Observer 和 Subiect之间的分割和松耦合,易于产生更细粒度且更易于复用的类。Mediator 模式的目的是集中而不是分布,它将维护一个约束的职责直接放在一个中间者中。

Command、Observer、Mediator 和 Chain ofResponsibility 等模式都涉及如何对发送者和接收者解耦,但各自有不同的权衡考虑。

Command 模式使用一个 Comand 对象来定义一个发送者和一个接收者之间的绑定关系,从而支持解耦。Command 对象提供了一个提交请求的简单接口(即 Execute 操作),将发送者和接收者之间的连接定义在一个对象,使得该发送者可以与不同的接收者一起工作,达到将发送者和接收者解耦,使发送者更易于复用。此外,可以复用Command 对象,用不同的发送者参数化一个接收者。

Observer 模式通过定义一个接口来通知目标中发生的改变,从而将发送者(目标)与接收者(观察者)解耦。Observer 定义了一个比 Command 更松的发送者-接收者绑定,这是因为一个目标可能有多个观察者,并且其数目可以在运行时变化。此模式中的Subiect 和 Observer 接口是为了处理 Subject 的变化而设计的,因此,当对象间有数据依赖时,最好用此模式来对它
们进行解耦。

Mediator 模式让对象通过一个 Mediator 对象间接相互引用,从而对它们进行解耦。一个Mediator 对象为各 Colleague 对象间的请求提供路由,并集中它们的通信。因此,各 Colleague对象仅能通过 Mediator 接口相互通信。Mediator 接口是固定的,为了增加灵活性,Mediator 可能不得不实现它自己的分发策略。可以用一定方式对请求编码并打包参数,使得Colleague 对象可以请求的操作数目不限。由于此模式将通信行为集中到一个类中而不是将其分布在各个子类中,所以它可以减少一个系统的子类生成。

Chain of Responsibility 模式通过沿一个潜在接收者链传递请求而将发送者与接收者解耦。
因为发送者和接收者之间的接口是固定的,责任链可能也需要一个定制的分发策略。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

老攀呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值