Python 设计模式(第2版) -- 第一部分(创建型模式)

Python 设计模式(第2版)

设计模式的主要特点如下所示:

  • 它们是语言无关的,可以用多种语言实现。
  • 它们是动态的,随时会有新的模式引入。
  • 它们可以进行定制,因此对开发人员非常有用。

设计模式的优点如下所示:

  • 它们可以在多个项目中重复使用。
  • 问题可以在架构级别得到解决。
  • 它们都经过了时间的验证和良好的证明,是开发人员和架构师的宝贵经验。
  • 它们具有可靠性和依赖性。

Python是一种动态语言。Python 的动态特性如下所示:

  • 类型或类是运行时对象。
  • 变量可以根据赋值来确定类型,并且类型可以在运行时改变。
  • 动态语言在类限制方面具有更大的灵活性。
  • 例如,在 Python 中,多态性是该语言所固有的,并没有诸如 private 和 protected 之类的关键字,因为默认情况下一切都是公共的。
  • 可以使用动态语言轻松实现设计模式的用例。

设计模式是由 GoF(Gang of Four)首先提出的,根据他们的观点,设计模式就是解决特定问题的解决方案。在他们的设计模式书中讲到了 23 种设计模式,并将它们分为三大类:

  • 创建型模式。
  • 结构型模式。
  • 行为型模式。

以下是创建型模式的性质:

  • 它们的运行机制基于对象的创建方式。
  • 它们将对象创建的细节隔离开来。
  • 代码与所创建的对象的类型无关。

以下是结构型模式的性质:

  • 它们致力于设计出能够通过组合获得更强大功能的对象和类的结构。
  • 重点是简化结构并识别类和对象之间的关系。
  • 它们主要关注类的继承和组合。

行为型模式具有下列性质:

  • 它们关注对象之间的交互以及对象的响应性。
  • 对象应该能够交互,同时仍然保持松散耦合。

首先来看看创建型设计模式有哪些。

单例模式

单例模式提供了这样一个机制,即确保类有且只有一个特定类型的对象,并提供全局访问点。因此,单例模式通常用于下列情形,例如日志记录或数据库操作、打印机后台处理程序,以及其他程序——该程序运行过程中只能生成一个实例,以避免对同一资源产生相互冲突的请求。

下面是基于 Python v3.5 的单例模式实现代码,它主要完成了两件事情。

1.只允许Singleton类生成一个实例。2.如果已经有一个实例了,我们会重复提供同一个对象。

class Singleton(object):

    def __new__(cls):
        if not hasattr(cls, 'instance'):
            cls.instance = super(Singleton, cls).__new__(cls)
        return cls.instance


s = Singleton()
print("Object created", s)

s1 = Singleton()
print("Object created", s1)

单例模式的用例之一就是懒汉式实例化。懒汉式实例化能够确保在实际需要时才创建对象。所以,懒汉式实例化是一种节约资源并仅在需要时才创建它们的方式。

class Singleton:
    __instance = None

    def __init__(self):
        if not Singleton.__instance:
            print(" __init__ method called..")
        else:
            print("Instance already created:", self.getInstance())

    @classmethod
    def getInstance(cls):
        if not cls.__instance:
            cls.__instance = Singleton()
        return cls.__instance


s = Singleton() ## class initialized, but object not created
print("Object created", Singleton.getInstance()) # Object gets createdhere
s1 = Singleton() ## instance already created

作为一个实际的用例,我们将通过一个数据库应用程序来展示单例的应用。这里不妨以需要对数据库进行多种读取和写入操作的云服务为例进行讲解。

import sqlite3

class MetaSingleton(type):

    _instances = {}
    
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(MetaSingleton, cls). __call__(*args, **kwargs)
        return cls._instances[cls]

class Database(metaclass=MetaSingleton):

    connection = None

    def connect(self):
        if self.connection is None:
            self.connection = sqlite3.connect("db.sqlite3")
            self.cursorobj = self.connection.cursor()
        return self.cursorobj

db1 = Database().connect()
db2 = Database().connect()

print ("Database Objects DB1", db1)
print ("Database Objects DB2", db2)

考虑另一种情况,即为基础设施提供运行状况监控服务(就像 Nagios 所作的那样)。首先创建了 HealthCheck 类,它作为单例实现。还要维护一个被监控的服务器列表。当一个服务器从这个列表中删除时,监控软件应该觉察到这一情况,并从被监控的服务器列表中将其删除。

class HealthCheck:

    _instance = None

    def __new__(cls, *args, **kwargs):
        if not HealthCheck._instance:
            HealthCheck._instance = super(HealthCheck, cls). __new__(cls, *args, **kwargs)
        return HealthCheck._instance

    def __init__(self):
        self._servers = []

    def addServer(self):
        self._servers.append("Server 1")
        self._servers.append("Server 2")
        self._servers.append("Server 3")
        self._servers.append("Server 4")

    def changeServer(self):
        self._servers.pop()
        self._servers.append("Server 5")

hc1 = HealthCheck()
hc2 = HealthCheck()

hc1.addServer()
print("Schedule health check for servers (1)..")
for i in range(4):
    print("Checking ", hc1._servers[i])

hc2.changeServer()
print("Schedule health check for servers (2)..")
for i in range(4):
    print("Checking ", hc2._servers[i])

单例模式的缺点:

  • 全局变量可能在某处已经被误改,但是开发人员仍然认为它们没有发生变化,而该变量还在应用程序的其他位置被使用。
  • 可能会对同一对象创建多个引用。由于单例只创建一个对象,因此这种情况下会对同一个对象创建多个引用。
  • 所有依赖于全局变量的类都会由于一个类的改变而紧密耦合为全局数据,从而可能在无意中影响另一个类。

工厂模式 – 建立创建对象的工厂

工厂具有下列优点:

  • 松耦合,即对象的创建可以独立于类的实现。
  • 客户端无需了解创建对象的类,但是照样可以使用它来创建对象。它只需要知道需要传递的接口、方法和参数,就能够创建所需类型的对象了。这简化了客户端的实现。
  • 可以轻松地在工厂中添加其他类来创建其他类型的对象,而这无需更改客户端代码。最简单的情况下,客户端只需要传递另一个参数就可以了。
  • 工厂还可以重用现有对象。但是,如果客户端直接创建对象的话,总是创建一个新对象。

工厂模式有 3 种变体,如下所示:

  • 简单工厂模式:允许接口创建对象,但不会暴露对象的创建逻辑。
  • 工厂方法模式:允许接口创建对象,但使用哪个类来创建对象,则是交由子类决定的。
  • 抽象工厂模式:抽象工厂是一个能够创建一系列相关的对象而无需指定/公开其具体类的接口。该模式能够提供其他工厂的对象,在其内部创建其他对象。

1.简单工厂模式

现在,让我们借助 Python v3.5 代码示例来进一步理解简单工厂模式。首先将创建一个名为 Animal 的抽象产品。Animal 是一个抽象的基类(ABCMeta 是 Python 的特殊元类,用来生成类 Abstract),它带有方法 do_say()。我们利用 Animal 接口创建了两种产品(Cat 和 Dog),并实现了 do_say() 方法来提供这些动物的相应的叫声。ForestFactory 是一个带有 make_sound() 方法的工厂。

from abc import ABCMeta, abstractmethod

class Animal(metaclass = ABCMeta):

    @abstractmethod
    def do_say(self):
        pass

class Dog(Animal):

    def do_say(self):
        print("Bhow Bhow! ! ")

class Cat(Animal):

    def do_say(self):
        print("Meow Meow! ! ")

## forest factory defined
class ForestFactory(object):

    def make_sound(self, object_type):
        return eval(object_type)().do_say()

## client code
if __name__ == '__main__':
    ff = ForestFactory()
    animal = input("Which animal should make_sound Dog or Cat? ")
    ff.make_sound(animal)

2.工厂方法模式

通过一个现实世界的场景来理解工厂方法的实现,假设想在不同类型的社交网络(例如 LinkedIn、Facebook 等)上为个人或公司建立简介。那么,每个简介都有某些特定的组成章节。在 LinkedIn 的简介中,有一个章节是关于个人申请的专利或出版作品的。在 Facebook 上,你将在相册中看到最近度假地点的照片区。此外,在这两个简介中,都有一个个人信息的区。

首先定义接口 Product,将创建一个 Section 抽象类来定义一个区是关于哪方面内容的,让它尽量保持简单,同时还提供一个抽象方法 describe()。然后,会创建多个 ConcreteProduct、PersonalSection、AlbumSection、PatentSection 和 PublicationSection 类。创建了一个名为 Profile 的抽象类Creator。Profile [Creator]抽象类提供了一个工厂方法,即 createProfile()。createProfile() 方法应该由 ConcreteClass 实现,来实际创建带有适当区的简介。创建了两个 ConcreteCreator 类,即 linkedin 和 facebook。每个类都实现 createProfile() 抽象方法。

from abc import ABCMeta, abstractmethod


class Section(metaclass=ABCMeta):

    @abstractmethod
    def describe(self):
        pass

class PersonalSection(Section):

    def describe(self):
        print("Personal Section")


class AlbumSection(Section):

    def describe(self):
        print("Album Section")


class PatentSection(Section):

    def describe(self):
        print("Patent Section")


class PublicationSection(Section):
    
    def describe(self):
        print("Publication Section")

class Profile(metaclass=ABCMeta):

    def __init__(self):
        self.sections = []
        self.createProfile()

    @abstractmethod
    def createProfile(self):
        pass

    def getSections(self):
        return self.sections
    
    def addSections(self, section):
        self.sections.append(section)

class linkedin(Profile):

    def createProfile(self):
        self.addSections(PersonalSection())
        self.addSections(PatentSection())
        self.addSections(PublicationSection())


class facebook(Profile):

    def createProfile(self):
        self.addSections(PersonalSection())
        self.addSections(AlbumSection())

if __name__ == '__main__':
    profile_type = input("Which Profile you'd like to create? [LinkedIn or FaceBook]")
    profile = eval(profile_type.lower())()
    print("Creating Profile..", type(profile). __name__)
    print("Profile has sections --", profile.getSections())

工厂方法模式的优点:

  • 它具有更大的灵活性,使得代码更加通用,因为它不是单纯地实例化某个类。这样,实现哪些类取决于接口(Product),而不是 ConcreteProduct类。
  • 它们是松耦合的,因为创建对象的代码与使用它的代码是分开的。客户端完全不需要关心要传递哪些参数以及需要实例化哪些类。由于添加新类更加容易,所以降低了维护成本。

3.抽象工厂模式

抽象工厂模式的主要目的是提供一个接口来创建一系列相关对象,而无需指定具体的类。

如何实现抽象工厂模式,讨论披萨饼店提供多种披萨饼订购的场景。加入开办了一家披萨店,供应美味的印式和美式披萨饼。为此,首先创建一个抽象基类 —— PizzaFactory。PizzaFactory 类有两个抽象方法即 createVegPizza() 和 createNonVegPizza(),它们需要通过 ConcreteFactory 实现。在这个例子中,我们创造了两个具体的工厂,分别名为 IndianPizzaFactory 和 USPizzaFactory。然后,将创建两个抽象类:VegPizza 和 NonVegPizza。再创建 DeluxVeggiePizza 和 MexicanVegPizza,并实现 prepare() 方法。接下来,我们来定义 ChickenPizza 和 HamPizza,并实现 server() 方法。

from abc import ABCMeta, abstractmethod


class PizzaFactory(metaclass=ABCMeta):

    @abstractmethod
    def createVegPizza(self):
        pass

    @abstractmethod
    def createNonVegPizza(self):
        pass

class IndianPizzaFactory(PizzaFactory):

    def createVegPizza(self):
        return DeluxVeggiePizza()

    def createNonVegPizza(self):
        return ChickenPizza()

class USPizzaFactory(PizzaFactory):
    def createVegPizza(self):
        return MexicanVegPizza()

    def createNonVegPizza(self):
        return HamPizza()

class VegPizza(metaclass=ABCMeta):

    @abstractmethod
    def prepare(self, VegPizza):
        pass

class NonVegPizza(metaclass=ABCMeta):

    @abstractmethod
    def serve(self, VegPizza):
        pass

class DeluxVeggiePizza(VegPizza):

    def prepare(self):
        print("Prepare ", type(self). __name__)

class ChickenPizza(NonVegPizza):

    def serve(self, VegPizza):
        print(type(self). __name__, " is served with Chicken on ", type(VegPizza). __name__)

class MexicanVegPizza(VegPizza):

    def prepare(self):
        print("Prepare ", type(self). __name__)

class HamPizza(NonVegPizza):

    def serve(self, VegPizza):
        print(type(self). __name__, " is served with Ham on ", type(VegPizza). __name__)

class PizzaStore:

    def __init__(self):
        pass

    def makePizzas(self):
        for factory in [IndianPizzaFactory(), USPizzaFactory()]:
            self.factory = factory
            self.NonVegPizza = self.factory.createNonVegPizza()
            self.VegPizza = self.factory.createVegPizza()
            self.VegPizza.prepare()
            self.NonVegPizza.serve(self.VegPizza)


pizza = PizzaStore()
pizza.makePizzas()

工厂方法和抽象工厂方法比较

工厂方法抽象工厂方法
向客户端开放了一个创建对象的方法包含一个或多个工厂方法来创建一个系列的相关对象
使用继承和子类决定要创建哪个对象使用组合将创建对象的任务委托给其他类
工厂方法用于创建一个产品用于创建相关产品的系列
  • 29
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
《精通Python设计模式 第2》是一本非常优秀的Python设计模式的指南。这本书由Dettori和Gabriele Tomassetti撰写,旨在帮助Python开发者更好地理解和应用设计模式。 这本书共分为11个章节,每个章节都涵盖了一个重要的设计模式或者相关主题。作者在书中详细解释了如何使用Python编写实际的示例代码,并给出了详细的解释和说明。这些示例代码不仅可以帮助读者理解每个设计模式的原理和应用场景,还可以帮助读者提高自己的编程技能。 《精通Python设计模式 第2》的每个章节都以问题和解决方案的形式展示。作者首先介绍了一个真实的问题,在问题背景的基础上,详细解释了设计模式的概念和原理,并给出了使用该设计模式解决问题的示例代码。通过这种方式,读者可以更好地理解设计模式的应用场景和使用方法。 此外,这本书还涵盖了一些高级主题,例如元类和装饰器的应用,以及如何使用设计模式构建可扩展的应用程序。这些高级主题对于有一定Python编程经验的读者来说非常有价值,可以帮助他们进一步提高自己的编程水平。 总之,《精通Python设计模式 第2》是一本非常优秀的Python设计模式的指南,适用于任何想要提高自己Python编程技能的读者。无论是初学者还是有经验的开发者,都能从这本书中学到很多有价值的知识和技巧。我强烈推荐这本书给所有对Python设计模式感兴趣的读者。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值