常见设计模式 (python代码实现)

Posted ExplorerMan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了常见设计模式 (python代码实现)相关的知识,希望对你有一定的参考价值。

1.创建型模式

单例模式

单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。

比如,某个服务器程序的配置信息存放在一个文件中,客户端通过一个 AppConfig 的类来读取配置文件的信息。如果在程序运行期间,有很多地方都需要使用配置文件的内容,也就是说,很多地方都需要创建 AppConfig 对象的实例,这就导致系统中存在多个 AppConfig 的实例对象,而这样会严重浪费内存资源,尤其是在配置文件内容很多的情况下。事实上,类似 AppConfig 这样的类,我们希望在程序运行期间只存在一个实例对象

复制代码
 1 class Singleton(object):
 2     def __init__(self):
 3         pass
 4 
 5     def __new__(cls, *args, **kwargs):
 6         if not hasattr(Singleton, "_instance"): # 反射
 7             Singleton._instance = object.__new__(cls)
 8         return Singleton._instance
 9 
10 obj1 = Singleton()
11 obj2 = Singleton()
12 print(obj1, obj2) #<__main__.Singleton object at 0x004415F0> <__main__.Singleton object at 0x004415F0>
复制代码

 

工厂模式

工厂模式是一个在软件开发中用来创建对象的设计模式。

工厂模式包涵一个超类。这个超类提供一个抽象化的接口来创建一个特定类型的对象,而不是决定哪个对象可以被创建。

为了实现此方法,需要创建一个工厂类创建并返回。 

当程序运行输入一个“类型”的时候,需要创建于此相应的对象。这就用到了工厂模式。在如此情形中,实现代码基于工厂模式,可以达到可扩展,可维护的代码。当增加一个新的类型,不在需要修改已存在的类,只增加能够产生新类型的子类。

简短的说,当以下情形可以使用工厂模式:

1.不知道用户想要创建什么样的对象

2.当你想要创建一个可扩展的关联在创建类与支持创建对象的类之间。

一个例子更能很好的理解以上的内容:

  1. 我们有一个基类Person ,包涵获取名字,性别的方法 。有两个子类male 和female,可以打招呼。还有一个工厂类。
  2.  工厂类有一个方法名getPerson有两个输入参数,名字和性别。
  3.  用户使用工厂类,通过调用getPerson方法。

在程序运行期间,用户传递性别给工厂,工厂创建一个与性别有关的对象。因此工厂类在运行期,决定了哪个对象应该被创建

复制代码
class Person:
    def __init__(self):
        self.name = None
        self.gender = None

    def getName(self):
        return self.name

    def getGender(self):
        return self.gender

class Male(Person):
    def __init__(self, name):
        print "Hello Mr." + name

class Female(Person):
    def __init__(self, name):
        print "Hello Miss." + name

class Factory:
    def getPerson(self, name, gender):
        if gender == ‘M\':
                return Male(name)
        if gender == \'F\':
            return Female(name)


if __name__ == \'__main__\':
    factory = Factory()
    person = factory.getPerson("Chetan", "M")
复制代码

建造者模式

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

相关模式:思路和模板方法模式很像,模板方法是封装算法流程,对某些细节,提供接口由子类修改,建造者模式更为高层一点,将所有细节都交由子类实现

一个例子更能很好的理解以上的内容:

1. 有一个接口类,定义创建对象的方法。一个指挥员类,接受创造者对象为参数。两个创造者类,创建对象方法相同,内部创建可自定义

2.一个指挥员,两个创造者(瘦子 胖子),指挥员可以指定由哪个创造者来创造

复制代码
from abc import ABCMeta, abstractmethod


class Builder():
    __metaclass__ = ABCMeta

    @abstractmethod
    def draw_left_arm(self):
        pass

    @abstractmethod
    def draw_right_arm(self):
        pass

    @abstractmethod
    def draw_left_foot(self):
        pass

    @abstractmethod
    def draw_right_foot(self):
        pass

    @abstractmethod
    def draw_head(self):
        pass

    @abstractmethod
    def draw_body(self):
        pass


class Thin(Builder):
    def draw_left_arm(self):
        print \'画左手\'

    def draw_right_arm(self):
        print \'画右手\'

    def draw_left_foot(self):
        print \'画左脚\'

    def draw_right_foot(self):
        print \'画右脚\'

    def draw_head(self):
        print \'画头\'

    def draw_body(self):
        print \'画瘦身体\'


class Fat(Builder):
    def draw_left_arm(self):
        print \'画左手\'

    def draw_right_arm(self):
        print \'画右手\'

    def draw_left_foot(self):
        print \'画左脚\'

    def draw_right_foot(self):
        print \'画右脚\'

    def draw_head(self):
        print \'画头\'

    def draw_body(self):
        print \'画胖身体\'


class Director():
    def __init__(self, person):
        self.person=person

    def draw(self):
        self.person.draw_left_arm()
        self.person.draw_right_arm()
        self.person.draw_left_foot()
        self.person.draw_right_foot()
        self.person.draw_head()
        self.person.draw_body()


if __name__==\'__main__\':
    thin=Thin()
    fat=Fat()
    director_thin=Director(thin)
    director_thin.draw()
    director_fat=Director(fat)
    director_fat.draw()
复制代码

 

原型模式

 

原型模式

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
原型模式本质就是克隆对象,所以在对象初始化操作比较复杂的情况下,很实用,能大大降低耗时,提高性能,因为“不用重新初始化对象,而是动态地获得对象运行时的状态”。

浅拷贝(Shallow Copy):指对象的字段被拷贝,而字段引用的对象不会被拷贝,拷贝的对象和源对象只是名称相同,但是他们共用一个实体。
深拷贝(deep copy):对对象实例中字段引用的对象也进行拷贝。

复制代码
import copy
from collections import OrderedDict


class Book:
    def __init__(self, name, authors, price, **rest):
        \'\'\'rest的例子有:出版商、长度、标签、出版日期\'\'\'
        self.name = name
        self.authors = authors
        self.price = price  # 单位为美元
        self.__dict__.update(rest)

    def __str__(self):
        mylist = []
        ordered = OrderedDict(sorted(self.__dict__.items()))
        for i in ordered.keys():
            mylist.append(\'{}: {}\'.format(i, ordered[i]))
            if i == \'price\':
                mylist.append(\'$\')
            mylist.append(\'\\n\')
            return \'\'.join(mylist)


class Prototype:
    def __init__(self):
        self.objects = dict()

    def register(self, identifier, obj):
        self.objects[identifier] = obj

    def unregister(self, identifier):
        del self.objects[identifier]

    def clone(self, identifier, **attr):
        found = self.objects.get(identifier)
        if not found:
            raise ValueError(\'Incorrect object identifier: {}\'.format(identifier))
        obj = copy.deepcopy(found)
        obj.__dict__.update(attr)
        return obj


def main():
    b1 = Book(\'The C Programming Language\', (\'Brian W. Kernighan\', \'Dennis M.Ritchie\'),
              price=118, publisher=\'Prentice Hall\', length=228, publication_date=\'1978-02-22\',
              tags=(\'C\', \'programming\', \'algorithms\', \'data structures\'))
    prototype = Prototype()
    cid = \'k&r-first\'
    prototype.register(cid, b1)
    b2 = prototype.clone(cid, name=\'The C Programming Language(ANSI)\', price=48.99,
                         length=274, publication_date=\'1988-04-01\', edition=2)
    for i in (b1, b2):
        print(i)
    print("ID b1 : {} != ID b2 : {}".format(id(b1), id(b2)))


if __name__ == \'__main__\':
    main()

"""
>>> python3 prototype.py
authors: (\'Brian W. Kernighan\', \'Dennis M. Ritchie\')
length: 228
name: The C Programming Language
price: 118$
publication_date: 1978-02-22
publisher: Prentice Hall
tags: (\'C\', \'programming\', \'algorithms\', \'data structures\')


authors: (\'Brian W. Kernighan\', \'Dennis M. Ritchie\')
edition: 2
length: 274
name: The C Programming Language (ANSI)
price: 48.99$
publication_date: 1988-04-01
publisher: Prentice Hall
tags: (\'C\', \'programming\', \'algorithms\', \'data structures\')

ID b1 : 140004970829304 != ID b2 : 140004970829472
"""
复制代码

 

2.结构型模式

适配器模式

所谓适配器模式是指是一种接口适配技术,它可通过某个类来使用另一个接口与之不兼容的类,运用此模式,两个类的接口都无需改动。

适配器模式主要应用于希望复用一些现存的类,但是接口又与复用环境要求不一致的情况,比如在需要对早期代码复用一些功能等应用上很有实际价值。

解释二:

适配器模式(Adapter Pattern):将一个类的接口转换成为客户希望的另外一个接口.Adapter Pattern使得原本由于接口不兼容而不能一起工作的那些类可以一起工作.
应用场景:系统数据和行为都正确,但接口不符合时,目的是使控制范围之外的一个原有对象与某个接口匹配,适配器模式主要应用于希望复用一些现存的类,但接口又与复用环境不一致的情况

适配器模式

复制代码
class Target(object):
    def request(self):
        print "普通请求"

class Adaptee(object):

    def specific_request(self):
        print "特殊请求"

class Adapter(Target):

    def __init__(self):
        self.adaptee = Adaptee()

    def request(self):
        self.adaptee.specific_request()

if __name__ == "__main__":
    target = Adapter()
    target.request()
复制代码

 

修饰器模式

该模式虽名为修饰器,但这并不意味着它应该只用于让产品看起来更漂亮。修饰器模式通常用于扩展一个对象的功能。这类扩展的实际例子有,给枪加一个消音器、使用不同的照相机镜头

复制代码
import functools
def memoize(fn):
    known = dict()
    @functools.wraps(fn)
    def memoizer(*args):
        if args not in known:
            known[args] = fn(*args)
        return known[args]
    return memoizer
@memoize
def nsum(n):
    \'\'\'返回前n个数字的和\'\'\'
    assert(n >= 0), \'n must be >= 0\'
    return 0 if n == 0 else n + nsum(n-1)
@memoize
def fibonacci(n):
    \'\'\'返回斐波那契数列的第n个数\'\'\'
    assert(n >= 0), \'n must be >= 0\'
    return n if n in (0, 1) else fibonacci(n-1) + fibonacci(n-2)
if __name__ == \'__main__\':
    from timeit import Timer
    measure = [ {\'exec\':\'fibonacci(100)\', \'import\':\'fibonacci\',
    \'func\':fibonacci},{\'exec\':\'nsum(200)\', \'import\':\'nsum\',
    \'func\':nsum} ]
    for m in measure:
        t = Timer(\'{}\'.format(m[\'exec\']), \'from __main__ import{}\'.format(m[\'import\']))
        print(\'name: {}, doc: {}, executing: {}, time:{}\'.format(m[\'func\'].__name__, m[\'func\'].__doc__,m[\'exec\'], t.timeit()))

"""
>>> python3 mymath.py
name: fibonacci, doc: Returns the nth number of the Fibonacci
sequence, executing: fibonacci(100), time: 0.4169441329995607
name: nsum, doc: Returns the sum of the first n numbers,
executing: nsum(200), time: 0.4160157349997462
"""
复制代码

 

 

外观模式

外观模式又叫做门面模式。在面向对象程序设计中,解耦是一种推崇的理念。但事实上由于某些系统中过于复杂,从而增加了客户端与子系统之间的耦合度。例如:在家观看多媒体影院时,更希望按下一个按钮就能实现影碟机,电视,音响的协同工作,而不是说每个机器都要操作一遍。这种情况下可以采用外观模式,即引入一个类对子系统进行包装,让客户端与其进行交互。

外观模式(Facade Pattern):外部与一个子系统的通信必须通过一个统一的外观对象进行,为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。外观模式又称为门面模式,它是一种对象结构型模式。

复制代码
from enum import Enum
from abc import ABCMeta, abstractmethod

State = Enum(\'State\', \'new running sleeping restart zombie\')


class User:
    pass


class Process:
    pass


class File:
    pass


class Server(metaclass=ABCMeta):
    @abstractmethod
    def __init__(self):
        pass

    def __str__(self):
        return self.name

    @abstractmethod
    def boot(self):
        pass

    @abstractmethod
    def kill(self, restart=True):
        pass


class FileServer(Server):
    def __init__(self):
        \'\'\'初始化文件服务进程要求的操作\'\'\'
        self.name = \'FileServer\'
        self.state = State.new

    def boot(self):
        print(\'booting the {}\'.format(self))
        \'\'\'启动文件服务进程要求的操作\'\'\'
        self.state = State.running

    def kill(self, restart=True):
        print(\'Killing {}\'.format(self))
        \'\'\'终止文件服务进程要求的操作\'\'\'
        self.state = State.restart if restart else State.zombie

    def create_file(self, user, name, permissions):
        \'\'\'检查访问权限的有效性、用户权限等\'\'\'
        print("trying to create the file \'{}\' for user \'{}\' with permissions{}".format(name, user, permissions))


class ProcessServer(Server):
    def __init__(self):
        \'\'\'初始化进程服务进程要求的操作\'\'\'
        self.name = \'ProcessServer\'
        self.state = State.new

    def boot(self):
        print(\'booting the {}\'.format(self))
        \'\'\'启动进程服务进程要求的操作\'\'\'
        self.state = State.running

    def kill(self, restart=True):
        print(\'Killing {}\'.format(self))
        \'\'\'终止进程服务进程要求的操作\'\'\'
        self.state = State.restart if restart else State.zombie

    def create_process(self, user, name):
        \'\'\'检查用户权限和生成PID等\'\'\'
        print("trying to create the process \'{}\' for user \'{}\'".format(name, user))


class WindowServer:
    pass


class NetworkServer:
    pass


class OperatingSystem:
    \'\'\'外观\'\'\'

    def __init__(self):
        self.fs = FileServer()
        self.ps = ProcessServer()

    def start(self):
        [i.boot() for i in (self.fs, self.ps)]

    def create_file(self, user, name, permissions):
        return self.fs.create_file(user, name, permissions)

    def create_process(self, user, name):
        return self.ps.create_process(user, name)


def main():
    os = OperatingSystem()
    os.start()
    os.create_file(\'foo\', \'hello\', \'-rw-r-r\')
    os.create_process(\'bar\', \'ls /tmp\')


if __name__ == \'__main__\':
    main()

"""
booting the FileServer
booting the ProcessServer
trying to create the file \'hello\' for user \'foo\' with permissions-rw-r-r
trying to create the process \'ls /tmp\' for user \'bar\'
"""
复制代码

 

享元模式

运用共享技术有效地支持大量细粒度的对象。
内部状态:享元对象中不会随环境改变而改变的共享部分。比如围棋棋子的颜色。
外部状态:随环境改变而改变、不可以共享的状态就是外部状态。比如围棋棋子的位置。

应用场景:程序中使用了大量的对象,如果删除对象的外部状态,可以用相对较少的共享对象取代很多组对象,就可以考虑使用享元模式。

复制代码
 1 import random
 2 from enum import Enum
 3 TreeType = Enum(\'TreeType\', \'apple_tree cherry_tree peach_tree\')
 4 
 5 class Tree:
 6     pool = dict()
 7     def __new__(cls, tree_type):
 8         obj = cls.pool.get(tree_type, None)
 9         if not obj:
10             obj = object.__new__(cls)
11             cls.pool[tree_type] = obj
12             obj.tree_type = tree_type
13         return obj
14 
15     def render(self, age, x, y):
16         print(\'render a tree of type {} and age {} at ({}, {})\'.format(self.tree_type, age, x, y))
17 
18 
19 def main():
20     rnd = random.Random()
21     age_min, age_max = 1, 30 # 单位为年
22     min_point, max_point = 0, 100
23     tree_counter = 0
24     for _ in range(10):
25         t1 = Tree(TreeType.apple_tree)
26         t1.render(rnd.randint(age_min, age_max),
27                 rnd.randint(min_point, max_point),
28                 rnd.randint(min_point, max_point))
29         tree_counter += 1
30     for _ in range(3):
31         t2 = Tree(TreeType.cherry_tree)
32         t2.render(rnd.randint(age_min, age_max),
33                 rnd.randint(min_point, max_point),
34                 rnd.randint(min_point, max_point))
35         tree_counter += 1
36     for _ in range(5):
37         t3 = Tree(TreeType.peach_tree)
38         t3.render(rnd.randint(age_min, age_max),
39                     rnd.randint(min_point, max_point),
40                     rnd.randint(min_point, max_point))
41         tree_counter += 1
42 
43     print(\'trees rendered: {}\'.format(tree_counter))
44     print(\'trees actually created: {}\'.format(len(Tree.pool)))
45     t4 = Tree(TreeType.cherry_tree)
46     t5 = Tree(TreeType.cherry_tree)
47     t6 = Tree(TreeType.apple_tree)
48     print(\'{} == {}? {}\'.format(id(t4), id(t5), id(t4) == id(t5)))
49     print(\'{} == {}? {}\'.format(id(t5), id(t6), id(t5) == id(t6)))
50 
51 main()
52 
53 """
54 render a tree of type TreeType.apple_tree and age 28 at (29, 80)
55 render a tree of type TreeType.apple_tree and age 28 at (38, 94)
56 render a tree of type TreeType.apple_tree and age 16 at (82, 84)
57 render a tree of type TreeType.apple_tree and age 18 at (43, 98)
58 render a tree of type TreeType.apple_tree and age 2 at (84, 72)
59 render a tree of type TreeType.apple_tree and age 16 at (89, 29)
60 render a tree of type TreeType.apple_tree and age 30 at (91, 53)
61 render a tree of type TreeType.apple_tree and age 12 at (92, 73)
62 render a tree of type TreeType.apple_tree and age 3 at (11, 54)
63 render a tree of type TreeType.apple_tree and age 1 at (34, 59)
64 render a tree of type TreeType.cherry_tree and age 11 at (67, 72)
65 render a tree of type TreeType.cherry_tree and age 27 at (65, 81)
66 render a tree of type TreeType.cherry_tree and age 27 at (10, 48)
67 render a tree of type TreeType.peach_tree and age 11 at (35, 38)
68 render a tree of type TreeType.peach_tree and age 3 at (58, 83)
69 render a tree of type TreeType.peach_tree and age 18 at (73, 50)
70 render a tree of type TreeType.peach_tree and age 24 at (94, 3)
71 render a tree of type TreeType.peach_tree and age 4 at (2, 9)
72 trees rendered: 18
73 trees actually created: 3
74 4866032 == 4866032? True
75 4866032 == 4742704? False
76 
77 """
复制代码

 

模型-视图-控制器模式

代理模式

3.行为型模式

责任链模式

命令模式

解释器模式

观察者模式

状态模式

 

策略模式

模板模式

 

以上是关于常见设计模式 (python代码实现)的主要内容,如果未能解决你的问题,请参考以下文章

13 个非常有用的 Python 代码片段

常见的代码片段

使用 Git 来管理 Xcode 中的代码片段

几条jQuery代码片段助力Web开发效率提升

MapReduce实现两表的Join--原理及python和java代码实现

结构型设计模式 Structural Patterns :适配器 Adapter(Python 实现)