C++语言的运行时多态性的基础是虚函数机制,指向基类的指针可以指向它的任何派生类,在实现设计模式时充分利用了C++这一特性,结合继承机制,建立类和对象的层次关系,使C++最大程度的具有动态特性,将绑定关系尽可能推迟到运行时确定。
在GoF的23种模式中,部分设计模式是专门为静态语言提出的,有些模式在动态语言中语言一级就提供直接的支持,如Command模式,动态语言提供的函数式编程将函数本身看作是类对象。
1. 工厂模式
用C++语言实现的工厂方法仍然存在局限性,这种局限性不利于构建可复用的软件。因为创建所有的产品类型都是通过Make接口的,为了保持Make接口的
返回值对所有产品的兼容性,就不得不迫使所有产品类型必须继承于一个公共的基类,然后Make接口返回该基类,这样保证了Make返回的类型都可以转换成
特定的产品类型。但是,同一系列不同类型的产品在逻辑上可能不存在明确的公共基类,比如MazeFactory中的Maze和Wall,而且,使用公共基类导致了大量的向下强制转换,这种转换往往是不安全的,有时还不可行。
Pyhon语言的动态类型特性为解决该问题提供良好的方案,Python允许一个变量在运行时绑定到不同类型的对象上,所以不必要求不同类型的产品具有公
共基类,Make接口不必声明其返回类型,调用时具体的返回值类型在运行时交给解释器去完成。Python实现工厂方法的代码如下:
class Maze:…
class Wall:…
…
class MazeFactory(object):
def make(self, typename, *args):
if typename == 'maze': return Maze()
elif typename == 'wall': return Wall()
elif typename == 'room':
return Room(args[0])
elif typename == 'door': ]
return Door(args[0], args[1])
self是MazeFactory实例对象的引用参数,typename标识创建对象的类型,*args是创建具体对象时所需的参数列表。魔法迷宫的代码
class EnchantedFactory(MazeFactory):
def make(self, typename, *args):
if typename == 'room': return EnchantedRoom(args[0] )
elif typename == 'door': return EnchantedDoor(args[0],args[1])
else: return super(EnchantedFactory, self).make(typename, args)
make方法中的return super(EnchantedFactory, self).make(typename, args)表示调用父类的操作创建其它类型的对象。
那么创建一个具体的EnchantedFactory实例的代码:
mf = EnchantedFactory()
mz = mf.make('maze')
r1 = mf.make('room', 1)
r2 = mf.make('room', 2)
dr = mf.make('door', r1, r2)
当需要在MazeFactory添加一个Trap新类型时,只需要在Make方法中添加标示新类型的参数即可:
elif typename == “trap”: return Trap()
这种做法不但保持了MazeFactory对外接口的稳定性,而且不需要类型的向下转换。但这里同样存在一个问题:每添加一个新类型,都要修改Make的实现代码。能不能不用修改Make的代码即可添加一个新类型呢?class Maze:…
class Wall:…
…
class MazeFactory(object):
def make(self, typename, *args):
if typename == 'maze': return Maze()
elif typename == 'wall': return Wall()
elif typename == 'room':
return Room(args[0])
elif typename == 'door': ]
return Door(args[0], args[1])
self是MazeFactory实例对象的引用参数,typename标识创建对象的类型,*args是创建具体对象时所需的参数列表。魔法迷宫的代码
class EnchantedFactory(MazeFactory):
def make(self, typename, *args):
if typename == 'room': return EnchantedRoom(args[0] )
elif typename == 'door': return EnchantedDoor(args[0],args[1])
else: return super(EnchantedFactory, self).make(typename, args)
make方法中的return super(EnchantedFactory, self).make(typename, args)表示调用父类的操作创建其它类型的对象。
那么创建一个具体的EnchantedFactory实例的代码:
mf = EnchantedFactory()
mz = mf.make('maze')
r1 = mf.make('room', 1)
r2 = mf.make('room', 2)
dr = mf.make('door', r1, r2)
当需要在MazeFactory添加一个Trap新类型时,只需要在Make方法中添加标示新类型的参数即可:
elif typename == “trap”: return Trap()
这种做法不但保持了MazeFactory对外接口的稳定性,而且不需要类型的向下转换。但这里同样存在一个问题:每添加一个新类型,都要修改Make的实现代码。能不能不用修改Make的代码即可添加一个新类型呢?class Maze:…
class Wall:…
…
class MazeFactory(object):
def make(self, typename, *args):
if typename == 'maze': return Maze()
elif typename == 'wall': return Wall()
elif typename == 'room':
return Room(args[0])
elif typename == 'door': ]
return Door(args[0], args[1])
self是MazeFactory实例对象的引用参数,typename标识创建对象的类型,*args是创建具体对象时所需的参数列表。魔法迷宫的代码
class EnchantedFactory(MazeFactory):
def make(self, typename, *args):
if typename == 'room': return EnchantedRoom(args[0] )
elif typename == 'door': return EnchantedDoor(args[0],args[1])
else: return super(EnchantedFactory, self).make(typename, args)
make方法中的return super(EnchantedFactory, self).make(typename, args)表示调用父类的操作创建其它类型的对象。
那么创建一个具体的EnchantedFactory实例的代码:
mf = EnchantedFactory()
mz = mf.make('maze')
r1 = mf.make('room', 1)
r2 = mf.make('room', 2)
dr = mf.make('door', r1, r2)
当需要在MazeFactory添加一个Trap新类型时,只需要在Make方法中添加标示新类型的参数即可:
elif typename == “trap”: return Trap()
这种做法不但保持了MazeFactory对外接口的稳定性,而且不需要类型的向下转换。但这里同样存在一个问题:每添加一个新类型,都要修改Make的实现代码。能不能不用修改Make的代码即可添加一个新类型呢?原型模式(Prototype)提供了一种更好的解决方案——编制产品字典。
2. 原型模式(Prototype)
原型模式使用一个原型实例指定创建对象的类型,并且通过复制原型创建新的对象。原型模式的优点是可以在运行时动态的增加和删除产品类型,减少了子类化,还
可以动态的配置应用程序。使用原型管理器(Prototype
Manager)可以方便实现运行时类型的增加和删除,管理器中有个类型的注册表,注册表是个关联存储结构的表,对于给定类型的键值可以唯一确定一个类
型,增加一个新类型时就是在表中注册该类型,客户程序在使用一个类型前先访问注册表检索它的原型。实现迷宫MazeFactory原型的Python代码
如下:
class MazeFactory:
def __init__(self):
self.index = {'maze': Maze,
'wall': Wall,
'room': Room,
'door': Door}
def make(self, typename, *args):
return apply(self.index[typename], args)
def registtype(self, typename, instance):
self.index[typename] = instance
def unregisttype(self, typename):
del self.index[typename]
在
MazeFactory中,数据成员self.index={…}是个字典类型,存放MazeFactory产品类型,方法registtype和
unregisttype实现了产品类型的动态增加和删除,参数instance表示需要添加或删除类型的实例名。假如创建了一个MazeFactory
实例mf=MazeFactory(),实例Trap的定义如下:
class Trap:
def __init__(self, radius, height):
self.radius = radius
self.height = height
…
向mf中添加实例Trap的代码:mf.registtype(‘trap’,Trap),而相应的删除代码为mf.unregisttype(‘trap’,Trap)。
显然,这种实现方式便于动态管理类型,具有良好的可扩展性。
3. 单件模式提供了一种将系统中类的实例个数限制为一个的机制,保证了一个类只有一个实例,并提供了该实例的一个全局访问点。程序的不同模块通常会共享同一个
对象。单件模式隐藏了实际的全局变量,对外提供了访问的接口,是一种很好的访问全局变量的方法。首先我们来看一下C++是怎样实现单件模式的。
Singleton类的定义如下:
class Singleton {
public:
static Singleton* Instance();
protected:
Singleton();
private:
static Singleton* _instance;
};
//对应的实现:
Singleton* Singleton::_instance = 0;
Singleton* Singleton::Instance ()
{
if (_instance == 0)
{
_instance = new Singleton;
}
return _instance;
}
在
单件类中,静态数据成员_instance指向已经创建的实例,静态成员函数Instance()为单件类提供了全局访问点,客户程序只能通过
Instance()接口创建单件类,如果_instance不为0,则直接返回已创建的实例,注意Singleton类的构造函数声明为
protected属性防止客户程序不通过Instance()接口创建它,保证了单件类的唯一性。
Python语言中的类和函数的定义可以在运行时改变,借助这一语言特性给出实现Singleton模式Python版本:
class Singleton(object):
def __new__(cls):
cls.instance = object.__new__(cls)
cls.__new__ = cls.Instance
cls.instance.init()
return cls.instance
def Instance(cls,type):
return cls.instance
Instance = classmethod(Instance)
def init(self):
pass
语句:cls.__new__ =
cls.getInstance是将getInstance赋给__new__方法,执行后,Singleton类的__new__方法变成了
getInstance。第一次创建Singleton实例对象时,调用__new__方法生成Singleton的一个新的实例,试图再次创建
Singleton实例对象时,调用的__new__的方法实际上被“偷偷的“调包成getInstance,__new__方法的代码不再被执行,而是
执行getInstance方法返回已经创建的实例对象cls.instance,从而保证了只存在一个Singleton实例对象。
cls.instance.init()说明了__new__方法在__init__之前调用,为了进一步初始化Singleton子类。
4. 代理模式(Proxy)
从面向对象设计的角度看,限制访问属性给一些旧问题提供了一种新的解决办法。代理模式就是一个很好的例子。代理模式用于隔离对象和访问它的客户,比如引用计数、不同等级的授权访问以及对象的惰性赋值等。代理模式的结构如下:
客户程序不需要直接访问实际的对象,换句话说,代理替代了实际的对象,客户通过代理去访问实际的对象。在C++中,这就意味着Proxy和
RealSubject必须要有一个公共的基类。在Python中,通过提供相同的方法接口,Proxy可以达到冒充Subject的效果。以下
Python代码中Proxy类是基于小型的通用包装类,它的主要功能就是为多个特定代理的实现提供一个基类,在Proxy类中可以重载
__gettattr__方法处理不同的方法。
#Proxy Base Class
class Proxy:
def __init__( self, subject ):
self.__subject = subject
def __getattr__( self, name ):
return getattr( self.__subject, name )
#Subject class
class RGB:
def __init__( self, red, green, blue ):
self.__red = red
self.__green = green
self.__blue = blue
def Red( self ):
return self.__red
def Green( self ):
return self.__green
def Blue( self ):
return self.__blue
# More specific proxy implementation
class NoBlueProxy( Proxy ):
def Blue( self ):
return 0
考虑以下情况:首先我们需要直接访问RGB类的实例对象,然后使用一个通用的代理实例作为一个包装类,最后传递给NoBlueProxy类:
>>> rgb = RGB( 100, 192, 240 )
>>> rgb.Red()
100
>>> proxy = Proxy( rgb )
>>> proxy.Green()
192
>>> noblue = NoBlueProxy( rgb )
>>> noblue.Green()
192
>>> noblue.Blue()
0
代理模式在Python中应用很广泛,Python语言提供的机制中有一些就是代理模式实现的,比如垃圾收集中的简单引用计数。
5. 命令模式(Command)
用过集成开发环境的人都知道,开发基于窗口图形界面的应用程序时,一般要用到按钮和菜单等控件对象响应用户的输入,但
在集成环境的工具箱提供的按钮和菜单并没有显式地实现该请求,也就是按钮和菜单不知道关于请求的操作和请求的接受者的任何信息,这些请求特定于具体应用,
只用控件的使用者才知道该由哪个对象响应哪个操作,工具箱的设计者无法知道请求的接受者和执行的操作。那么工具箱的设计者是如何实现按钮和菜单的这种功能
的呢?用Command模式。
Command模式解耦了调用操作的对象(如按钮、菜单)和实现该操作的对象(如文档)。C++利用继承组合机制实
现Command模式,通过定义一个带有Execute接口的Command抽象类,特定应用相关的Command派生于此类,在Command类的子类
显式定义接受者的对象。Command类在调用操作的对象(Invoker)和实现该操作的对象(Receiver)之间充当了桥梁作用,
Invoker请求某个Command类,由Command类的Execute接口执行Receiver的具体操作并将返回结果告诉Invoker,对于
Invoker根本不知道是谁执行了该操作,也不需要知道,从而实现了两者的解耦。而Python具有运行时可以改变类的结构、函数的定义的动态特性,很
简单地就实现了Command模式:
class Button:
def click(self):pass
class document:
def open(self):
print "open document..."
btn = Button()
doc = document()
btn.click = doc.open
执行btn.click()时实际上相当于调用了doc.open()的方法,实现了Button和document两者的解耦,比C++的继承组合机制要简单。
有
时一个按钮要求执行一系列命令,这种宏命令(MacroCommand)在应用中也是很常见的。Python支持lamda匿名函数定义,运用函数式编程
方式也可以很简便的实现MacroCommand模式。我们先来看看用C++是怎样实现的。C++用一个命令列表管理命令系列,执行宏命令实际上是遍历一
次命令列表:
class Command {
public:
virtual ~Command();
virtual void Execute() = 0;
protected:
Command();
};
class MacroCommand : public Command {
public:
MacroCommand();
virtual ~MacroCommand();
virtual void Add(Command*);
virtual void Remove(Command*);
virtual void Execute();
private:
List<Command*>* _cmds;//命令列表
};
void MacroCommand::Execute ()
{
ListIterator<Command*> i(_cmds);
for (i.First(); !i.IsDone(); i.Next()) //遍历命令列表
{
Command* c = i.CurrentItem();
c->Execute();
}
}
在Python中,假设已经定义了一个Button类和一个Document类,Button类有一个Click方法,Document包含Paste和Replace方法,以下代码实现了点击按钮后同时执行Paste和Replace操作:
doc = Document()
btn = Button()
macrcocmd = lambda cmds : map(lambda cmd:cmd(),cmds)
btn.Click = lambda: macrcocmd([doc.Paste, doc.Replace])
函
数map是个Python内置函数,其作用是将列表cmds作为参数传递给匿名函数lambda cmd :
cmd()执行并返回一个元组作为匿名函数macrocmd的参数,调用btn.Click()执行了匿名函数macrocmd(),该函数接受元组
[doc.Paste, doc.Replace] 作为它的输入参数,执行doc.Paste()和doc.Replace()命令。
总结:
Python的灵活性和动态性为实现一些不同的、优美的解决方案提供了一个良好的基础。Python的无类型化解决了静态语言实现工厂方法中需要不安全的
强制类型转换等问题,既减少了程序中类的设计,又避免了静态语言中的向下转换问题。原型模式为创建不同类型的对象提供一种更好的方法,利用了Python
可以运行时改变类的定义的特性,可以动态地增删类型,提高了程序的可扩展性。而单件模式则利用了类的静态方法__new__以及动态改变类的结构和定义的
特点,巧妙地实现了限制了单件类的实例对象个数的功能。在Proxy模式中,充分体现了Python中同一个类的多个实例对象之间可以拥有不同的结构以及
可以监视和限制属性访问的语言机制,这些机制为个实现多个通用类提供了一个相当完美的解决方法。在C++中不支持匿名函数,命令模式初步展现了
Python中lamda匿名函数在实现某些问题的简便性。其他的模式,如职责链(Chain Of
Responsibility)、策略模式(Strategy)、装饰模式(Decorator),利用Python语言特性,也可以写出Python的
实现版本。
分享到:
相关推荐
《Go语言设计模式》这本书系统性地介绍了在Go语言中应用设计模式的方法与实践。本书不仅涉及了Go语言的基础入门知识,还深入到了函数编程、面向接口并发编程等多个层面,旨在帮助读者打通学习线路,从而精通Go语言。...
《设计模式——Java语言中的应用》是一本专为Java开发者深入理解面向对象设计而编写的经典书籍。设计模式是软件工程领域中经过实践验证的、解决常见问题的有效方案,它们代表了在特定上下文中,针对特定问题的最优...
本书《JavaScript设计模式与开发实践》是JavaScript语言的设计模式和开发实践的指南,旨在帮助初、中、高级Web前端开发人员和想往架构师晋级的中高级程序员,掌握JavaScript设计模式和开发实践的知识。本书共分为三...
在实际应用中,设计模式并非孤立存在,而是经常组合使用,例如“建造者模式”与“工厂模式”的结合,或者“外观模式”与“适配器模式”的配合,以解决更复杂的问题。在Java EE环境中,如Spring框架中,设计模式的...
通过学习和应用这些设计模式,开发者不仅可以提高代码的可读性、可维护性和可扩展性,还能提升团队间的沟通效率,因为设计模式是软件工程中的通用语言。对于任何有志于提升软件开发水平的人来说,理解和掌握设计模式...
在软件开发中,设计模式是解决常见问题的模板或最佳实践,它们被广泛应用于各种编程语言,包括PHP。本资源“用PHP语言实现16个设计模式.zip”提供了关于如何在PHP环境中应用这些模式的详细指导。以下是这16个设计...
在本书中,作者使用了轻松、有趣的语言来讲解设计模式,通过故事和示例来帮助读者更好地理解设计模式的思想和方法论。作者还提供了详细的代码实现和示例,帮助读者更好地掌握设计模式的应用。 本书是一本非常实用的...
《Java设计模式(第2版)》则更侧重于Java语言实现的设计模式,它涵盖了GoF模式的实现细节,同时可能还会涉及一些Java特有的模式,如枚举单例、注解驱动的设计模式等。此书可能会对每种模式进行深入探讨,包括其优缺点...
这将有助于提升开发者的编程技巧,使他们能够编写出更高效、更具可读性的代码,同时也有利于团队间的沟通与协作,因为设计模式提供了通用的语言和思路,使得复杂系统的设计和维护变得更加有条不紊。
章节介绍:1、爪哇语言结构性模式之变压器模式介绍 2、爪哇语言抽象工厂创立性模式介绍 3、工厂方法创立性模式介绍 4、单态创立性模式介绍 5、单态创立性模式介绍 6、观察者模式介绍7、责任链模式 8、设计模式之...
C++作为一种强大的面向对象编程语言,设计模式在其中的应用尤为广泛。C++的模板、继承和多态特性使得实现设计模式更加灵活。K.Eckel的《Thinking in C++》是学习C++的优秀教程,书中也涵盖了设计模式的讲解,帮助...
根据给定文件的内容,我们可以整理出Go语言实现的两种设计模式:简单工厂模式和策略模式。下面将详细说明这两种模式在Go语言中的实现方式和应用场景。 ### 简单工厂模式 简单工厂模式是一种创建型设计模式,它提供...
在Java编程语言中,设计模式的应用可以极大地提升代码的可读性、可维护性和可扩展性。本篇主要针对23种经典的设计模式进行深入探讨,并以Java语言为背景,解析它们在实际开发中的应用。 首先,我们有三种主要的设计...
### 软件架构与设计模式:深入理解与实践 #### 设计模式的定义与重要性 设计模式是在软件设计过程中解决常见问题的一种通用解决方案。它不仅是一种代码编写技巧,更是一种思考方式,帮助开发者在面对复杂问题时,...
**UML(统一建模语言)与设计模式**是软件工程中的两个重要概念,它们在构建高质量、可维护和可扩展的系统中起着至关重要的作用。UML是一种图形化语言,用于描述软件系统的结构和行为,而设计模式是解决常见软件设计...
《Head First设计模式》是一本深受开发者喜爱的经典书籍,它以独特且易于理解的方式介绍了设计模式这一复杂的...学习设计模式不仅能够提高代码质量,还能促进团队间的沟通,因为它们提供了一种共同的语言和思考方式。
Go Design Patterns will provide readers with a reference point to software design patterns and CSP concurrency design patterns to help them build applications in a more idiomatic, robust, and ...
Java语言特性与设计模式 Java语言是现代计算机科学中最重要的编程语言之一,其设计模式对程序设计语言的发展产生了深远的影响。下面是Java语言特性与设计模式的知识点摘要: 1.冯诺依曼计算机及其体系结构:冯...
'Java 设计模式:23 种设计模式全面解析(C 语言中文网).epub' 'JSP 教程:1 天玩转 JSP 网站开发技术(C 语言中文网).epub' 'Linux vi 命令 30 分钟入门教程(C 语言中文网).epub' 'Python 基础教程(C 语言中文...