GOF《Design Patterns》第三章
*GOF巨作《Design Patterns》毫无疑问是设计模式的圣经,然而“从风格上讲,该书与其说是为学习者而写作的教程范本,还不如说是给学术界人士看的学术报告,严谨有余,生动不足。”〔孟岩〕本系列将《Design Patterns》中文版(结合英文版)中重要句子按句解析,作为自学笔记也给新接触设计模式的朋友一点借鉴。文中原文以粗体标出。我自己不明白的地方以〔TODO:〕标出,希望高手多多指点。
创建型模式抽象了实例化过程。两个问题:
-
何谓实例化过程?
-
什么叫做抽象?
所谓实例化过程,说白了就是创建对象的过程。在面向对象语言中,一般是指通过构造器(constructor)创建对象的过程。在Java和C#中通常用new这个关键字来创建对象,当然还有通过反射的方法,后者用的较少。一个对象实例化结束后并不一定就可以使用了,对于复杂的对象有时候还需要做一些装配,有时候也叫做初始化。后面我们说到实例化没有特殊说明,就是指的用new这个关键创建对象。[e.g instance = new SomeConcreteClass();]
这个过程如何抽象呢?所谓抽象就是掩盖具体的东西,这里面那个东西是具体的概念呢?显然是构造器,也就是SomeConcreteClass这个类。这是一个具体类,当我们的代码里面出现具体类的时候,我们就依赖了具体实现,这违背了DIP原则。抽象就是客户端只了解自己需要某个实例,但是不了解改实例的创建过程,或者说不了解其构造函数的签名(包括构造器的名字即类名和构造参数)。
它们帮助一个系统独立于如何创建、组合和表示它的那些对象。原文是:They help make a system independent of how it’s objects created, composed and represented.这句话说明创建型模式的作用是使得系统独立于系统中对象的创建,组合和表示。从这一点上看,上一句说抽象了实例化过程中的实例化过程是包含装配过程(组合)的。创建型模式帮助系统独立于其对象的创建和组合是容易理解的。比如Factory Method通过一个返回接口的方法来返回对象实例,客户端并不了解对象的创建过程;Builder模式隔离了对象的各个部分的创建和其组合过程。那么独立于表示如何理解呢?“表示”在这里可以理解为对同一个接口的不同实现。
[e.g. (感谢Lenny Primark)
List<Number> numbers = numbersFactory.createNumbers(5, 10); // create five numbers with values of 10
工厂方法返回的实例可能是如下的任何一个“表示”:
-
it can give you LinkedList<Number> or ArrayList<Number>
-
it can give you List<IntegerNumber> or List<DoubleNumber>
-
or any other combination.
创建型模式返回给你的是借口,而实现和实际数据的表示细节则被隐藏了。]
一个类创建型模式使用继承改变被实例化的类,而一个对象创建型模式将实例化委托给另一个对象。
随着系统演化得越来越依赖与对象复合而不是类继承,创建型模式变得更为重要。首先给我们指明了演化的方向,越来越依赖于对象复合而不是继承,然后在这个前提下,应用创建型模式,我想这是鼓励对象创建型模式。
当这种情况发生时,重心从对一组固定行为的硬编码(hard-coding)转移为定义一个较小的基本行为集,这些行为可以被组合成任意数目的更复杂的行为。类继承实际上是“静态的”固定的行为,是在编译期确定了的行为,而对象复合追求的是通过较小的基本行为集组合成复杂的行为,通常是支持运行时改变的组合方式的。
这样创建有特定行为的对象要求的不仅仅是实例化一个类。即,还有可能要负责装配过程。
在这些模式中有两个不断出现的主旋律。第一,它们都将关于该系统使用哪些具体的类的信息封装起来。第二,它们隐藏了这些类的实例是如何被创建和放在一起的。第一个里面封装也就是说,客户代码不了解自己使用的具体类是什么。第二个就很明白了,但是与第一段中相比,没有提到“represented”(表示)这个意思。
整个系统关于这些对象所知道的是有抽象类所定义的接口。这里整个系统实际上说的是客户代码,整个系统如果包含具体类在内当然要了解具体类了。
因此创建型模式在什么被创建,谁创建它,它是怎样被创建的,已经何时创建这些方面给予你很大的灵活性。什么被创建:通过产品接口隐藏具体类。谁创建它:通过工厂接口隐藏具体工厂。它是怎样被创建:Builder抽象方法隐藏创建过程。何时创建:单件模式隐藏创建时机。〔TODO:这里还要加上其他几个模式分别封装什么。〕
它们允许你用结构和功能差别很大的“产品”对象配置一个系统。配置可以是静态的(即在编译时指定),也可以是动态的(在运行时)。结构和功能差别很大的“产品”应该实现相同的接口。
有时创建型模式是相互竞争的。例如,在有些情况下Prototype或Abstract Factory用起来都很好。而在另外一些情况下它们是互补的:Builder可以用其他模式去实现某个构件的创建。Prototype可以在它的实现中使用Singleton。
接下来作者举了一个例子,这里不重述了。但是还是有几点需要注意。
作者把Enter这个方法放入MapSite中,而不是某个叫做Player的类中。这里面包含了很多的智慧和经验。当然我不排除Player类中应该包含一个类似与Move之类的方法,但是这个方法应该调用Mapsite的Enter方法。试想,对于Move这个动作和其响应如果全部放在Player这个类中完成将是如下的效果:
[
public void Move(Direction d)
{
if(currentRoom.GetSide(d) is Room){}
else if (currentRoom.GetSide(d) is Wall){}
else if (currentRoom.GetSide(d) is Door)
{
if (door.isOpen){}
else{}
}
}
] 毫无疑问这种方式的可读性,扩展性都很差。Enter为更复杂的游戏操作提供了一个简单基础。在一个真正的游戏中,Enter可以将游戏者对象作为一个参数。
作者在给出CreateMaze方法之后的评价,考虑到这个函数所做的仅是创建一个有两个房间的迷宫,它是相当复杂的。这个复杂性是因为客户端负责了太多的职能。Room的构造器可以提前用墙壁来初始化房间的每一面。这样会使得客户端代码稍微简单一些。这个成员函数真正的问题不在于它的大小而在于它不灵活。
创建型模式显示如何使得这个设计更灵活,但未必会更小。所以使用模式的时候,要抛弃这个观点,认为好的设计是小的设计,而是灵活的可扩展的设计。
当前的设计不易扩展,其最大的障碍是对被实例化的类进行硬编码。在本章导论部分的最后,作者给出了创建型模式的解决方案。
-
Factory Method:CreateMaze调用虚函数而不是通过new来创建Room,Wall和Door。Factory Method实际上简单的令你惊讶!任何时候,当你存在一个虚拟的(抽象的)函数其返回值是一个接口(抽象类),你就在使用Factory Method了。(插一句,理解一个模式的简单性有时候比理解其复杂性更重要,很多人对Factory Method模式敬而远之,就是没有理解其简单性。)建议您参考一下我的另一篇文章:《没有Factory这个模式》。
-
Abstract Factory:CreateMaze不是调用自己的虚函数,而是从外界传入一个对象(抽象工厂)给它。CreateMaze方法调用这个对象的方法(也是虚方法)来创建Room,Wall和Door。
-
Builder:(TODO:等我看完了再补充:)
-
Prototype:(TODO:等我看完了再补充:)
-
Singleton:保证每个游戏中仅有一个迷宫,而且所有的对象都可以迅速访问它――二不需要求助于全局变量或函数。Singleton也使得迷宫易于扩展或者替换,且不需要变动已有代码。Singleton如此受到
争议,我就不多说了。作者在最后加一句“易于扩展或者替换”,还强调“不需要变动已有代码”确实让人不解。(TODO:哪位高手出来解释一下?)
分享到:
相关推荐
1. **创建型模式**(Creational Patterns) 2. **结构型模式**(Structural Patterns) 3. **行为型模式**(Behavioral Patterns) 每种模式都有详细的介绍,包括模式的目的、应用场景、实现方式及其优缺点等。 #### 五...
1. **工厂模式**:工厂模式是一种创建型设计模式,它提供了一种创建对象的最佳方式。在Go中,我们可以使用接口和结构体组合来实现简单工厂或者抽象工厂模式,以便在不指定具体类的情况下创建对象。 2. **单例模式**...
标题中的"DesignPatterns.pdf_objects_designpatterns_"暗示了这可能是一个关于面向对象设计模式的PDF文档,而描述中的"Design patterns elements of reusable objects"进一步确认了这一点。设计模式的核心理念在于...
4. **设计模式分类**:设计模式通常分为三类:创建型模式(如单例模式、抽象工厂模式等)、结构型模式(如代理模式、装饰器模式等)和行为型模式(如策略模式、职责链模式等)。每种模式都有其特定的用途和应用场景...
在嵌入式系统领域,设计模式分为几种类型:创建型模式、结构型模式、行为型模式。创建型模式关注对象创建,其设计目的是在不暴露创建逻辑的情况下增加和配置对象。结构型模式讨论如何组合类和对象以获得更大的结构,...
### 设计模式在Java中的应用 #### 设计模式概述与本书背景 ...无论是创建型模式还是结构型模式,都能帮助开发者构建更加健壮、灵活的软件系统。后续章节将继续探讨更多设计模式的具体实现和应用场景。
工厂模式是所有 creational patterns(创建型模式)的基础,有助于解耦代码,降低耦合度。 3. **抽象工厂模式**:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们的具体类。这个模式常用于跨平台或者...
1. 创建型模式:主要解决对象的创建问题,目的是创建型模式在不暴露创建逻辑的情况下,使客户端可以创建对象。 - 工厂方法模式(Factory Method):定义一个用于创建对象的接口,让子类决定实例化哪一个类。 - ...
1. 创建型模式(Creational Patterns): - 单例模式(Singleton):确保一个类只有一个实例,并提供一个全局访问点。 - 工厂方法模式(Factory Method):定义一个用于创建对象的接口,让子类决定实例化哪一个类...
设计模式分为三类:创建型模式、结构型模式和行为型模式。这些模式帮助开发者在面向对象编程中提高代码的可读性、可维护性和可扩展性。 **1. 创建型模式** - **单例模式**:确保一个类只有一个实例,并提供全局访问...
《MongoDB应用设计模式:MongoDB Applied Design Patterns》这本书深入探讨了在实际开发中如何有效地利用MongoDB的优势来构建高效、可靠且易于维护的数据存储解决方案。 在设计模式方面,书中可能涵盖了以下关键...
- **创建型模式**:关注于对象的创建机制,提供了一种在合适的时刻创建对象的方式。例如工厂方法模式(Factory Method)、抽象工厂模式(Abstract Factory)等。 - **结构型模式**:用于处理类和对象的组合方式,让...
"php设计模式-designpatterns-php.zip"这个压缩包很可能包含了关于如何在PHP项目中应用设计模式的资料,特别是针对"designpatterns-php-master"这个文件名,我们可以推测这可能是一个关于PHP设计模式的开源项目或...
1. **创建型模式**: - 单例模式(Singleton):保证一个类只有一个实例,并提供全局访问点。 - 工厂方法模式(Factory Method):定义一个用于创建对象的接口,让子类决定实例化哪一个类。 - 抽象工厂模式...
1. **工厂模式**:这是一种创建型设计模式,它提供了一个创建对象的接口,但允许子类决定实例化哪一个类。这样,工厂模式可以使系统在不指定具体类的情况下引用对象。 2. **单例模式**:确保一个类只有一个实例,并...
3. 观察者模式:定义对象间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。 4. 装饰器模式:动态地给对象添加一些额外的职责,提供了一种比继承更灵活的方式来扩展对象...
1. 创建型模式(Creational Patterns):这类模式涉及到对象的创建,例如单例模式(Singleton)、工厂方法模式(Factory Method)、抽象工厂模式(Abstract Factory)、建造者模式(Builder)和原型模式(Prototype...