0%

大话设计模式

![logo](/images/Design-Pattern / 大话设计模式_logo.jpg)

策略模式(Strategy)+ 简单工厂模式

它定义了算法家族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。

CashContext 定义了一系列的可供重用的算法或行为,策略模式封装了变化。

在基本的策略模式中,选择所用具体实现的职责由客户端对象承担,并转给策略模式的 Context 对象。这本身并没有解除客户端需要选择判断的压力,而策略模式与简单工厂结合后,选择具体实现的职责也可以由 Context 来承担,这就最大化地减轻了客户端的职责。

不过,这依然不完美,在 CashContext 里还是用到了 switch,也就是说,如果我们需要增加一种算法,就必须更改 CashContext 中的 switch 代码,这违反了开放封闭的原则。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import abc


class CashSuper:
__metaclass__ = abc.ABCMeta

@abc.abstractmethod
def accept_cash(self, c):
"""caculate cash"""


class CashNormal(CashSuper):
def accept_cash(self, c):
return c


class CashRebate(CashSuper):
def __init__(self, r):
self.rebate = r

def accept_cash(self, c):
return c * self.rebate


class CashReturn(CashSuper):
def __init__(self, c, r):
self.condition = c
self.money = r

def accept_cash(self, c):
return c - int(c / self.condition) * self.money


class CashContext:
def __init__(self, t, s):
self.cash = None
if t == "normal":
self.cash = CashNormal()
elif t == "rebate":
self.cash = CashRebate(float(s))
elif t == "return":
args = s.split(' ')
self.cash = CashReturn(float(args[0]), float(args[1]))

def get_result(self, c):
return self.cash.accept_cash(c)


if __name__ == "__main__":
cash_context = CashContext("normal", "")
print(cash_context.get_result(1000))

cash_context = CashContext("rebate", "0.8")
print(cash_context.get_result(1000))

cash_context = CashContext("return", "300 100")
print(cash_context.get_result(1000))

装饰器模式(Decorator)

装饰模式是为已有功能动态地添加更多功能的一种方式。

什么时候用?
当系统需要新功能的时候,向旧类中添加新的代码,这些新加的代码通常装饰了原有类的核心职责或主要行为,在主类中加入新的方法和逻辑,从而增加了主类的复杂度,而这些新加入的东西仅仅是为了满足一些只在某种特定情况下才会执行的特殊行为的需要。而装饰模式却提供了一个非常好的解决方案,它把每个要装饰的功能放在单独的类中,并让这个类包装它所要装饰的对象。因此,当需要执行特殊行为时,客户代码就可以在运行时根据需要有选择地、按顺序地试用装饰功能包装对象了。

好处:

  • 把类中的装饰功能从类中搬移去除,这样可以简化原有的类;
  • 有效地把类的核心职责和装饰功能区分开了。而且可以去除相关类中重复的装饰逻辑。

动态的给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class Person:
def show(self):
print("person")


class Finery(Person):
def __init__(self, c):
self.component = c

def show(self):
"""finery show"""


class Tie(Finery):
def show(self):
print("tie")
self.component.show()


class Suit(Finery):
def show(self):
print("suit")
self.component.show()


class Shoes(Finery):
def show(self):
print("shoes")
self.component.show()


if __name__ == "__main__":
person = Person()
tie = Tie(person)
suit = Suit(tie)
shoes = Shoes(suit)
shoes.show()

代理模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import abc


class GiveGift:
__metaclass__ = abc.ABCMeta

@abc.abstractmethod
def __init__(self, sg):
"""init"""

@abc.abstractmethod
def give_dolls(self):
"""give dolls"""

@abc.abstractmethod
def give_flowers(self):
"""give flowers"""


class SchoolGirl:
def __init__(self, n):
self.name = n


class Pursuit(GiveGift):
def __init__(self, sg):
GiveGift.__init__(self, sg)
self.school_girl = sg

def give_dolls(self):
print("give" + self.school_girl.name + "dolls")

def give_flowers(self):
print("give" + self.school_girl.name + "flowers")


class Proxy(GiveGift):
def __init__(self, sg):
GiveGift.__init__(self, sg)
self.pursuit = Pursuit(sg)

def give_dolls(self):
self.pursuit.give_dolls()

def give_flowers(self):
self.pursuit.give_flowers()


if __name__ == "__main__":
school_girl = SchoolGirl("Alice")
proxy = Proxy(school_girl)
proxy.give_flowers()
proxy.give_dolls()

工厂方法

简单工厂模式最大的优点在于工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类,对于客户端来说,去除了与具体产品的依赖。就像计算器一样,只需要把 + 给工厂,工厂自动就给出了相应的实例,但问题也在这里,如果要加一个功能,一定需要给工厂类的方法里加 Case 分支条件的,这可不是好办法,这就等于说,我们不但对扩展开放了,对修改也开放了,这样就违背了开放封闭的原则。

于是,工厂方法来了。

工厂方法模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类。

根据依赖倒转原则,把工厂类抽象出一个接口,这个接口只要一个方法,就是创建抽象产品的工厂方法。然后所有要生产具体类的工厂,就去实现这个接口。于是我们要增加新功能时,就不需要更改原有的工厂类,只需要增加此功能的运算类和相应的工厂类就可以了。

这样整个工厂和产品体系都没有修改的变化,而只是扩展的变化,这就完全符合了开放封闭的原则。

简单工厂 vs 工厂方法

工厂方法模式实现时,客户端需要决定实例化哪一个工厂来实现运算类,选择判断的问题还是存在的,也就是说,工厂方法把简单工厂的内部逻辑判断移到了客户端代码来进行。你想要加功能,本来是改工厂类,而现在是修改客户端。

工厂方法克服了简单工厂违背开放封闭原则的缺点,又保持了封装对象创建过程的优点。它们都是集中封装了对象的创建,使得要更换对象时,不需要做大的改动就可实现,降低了客户程序与产品对象的耦合。

工厂方法模式是简单工厂模式的进一步抽象和推广,由于使用了多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。

但缺点是由于每加一个产品,就需要加一个产品工厂的类,增加了额外的开发量。

所以还不是最佳的做法,利用 “反射” 可以解决避免分支判断的问题。不过今天还是不急,等以后再谈。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import abc


class Leifeng:
__metaclass__ = abc.ABCMeta

@abc.abstractmethod
def wash(self):
""""wash"""

@abc.abstractmethod
def sweep(self):
"""sweep"""

@abc.abstractmethod
def buy_rice(self):
"""buy rice"""


class Undergraduate(Leifeng):
def wash(self):
print "undergraduate wash"

def sweep(self):
print "undergraduate sweep"

def buy_rice(self):
print "undergraduate buy rice"


class Volunteer(Leifeng):
def wash(self):
print "volunteer wash"

def sweep(self):
print "volunteer sweep"

def buy_rice(self):
print "volunteer buy rice"


class IFactory:
__metaclass__ = abc.ABCMeta

@abc.abstractmethod
def CreateLeifeng(self):
"""create class leifeng"""


class UndergraduateFactory(IFactory):
def CreateLeifeng(self):
return Undergraduate()


class VolunteerFactory(IFactory):
def CreateLeifeng(self):
return Volunteer()


if __name__ == "__main__":
# create undergraduate to sweep
i_factory = UndergraduateFactory()
leifeng = i_factory.CreateLeifeng()
leifeng.sweep()

# create volunteer to wash
i_factory = VolunteerFactory() # just replace UndergraduateFactory with VolunteerFactory
leifeng = i_factory.CreateLeifeng()
leifeng.wash()

原型模式(Prototype)

原型模式其实就是从一个对象再创建另外一个可定制的对象,而且不需要知道任何创建的细节。

需要注意深复制和浅复制:

  • 浅复制:被复制的对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象;
  • 深复制:吧引用对象的变量指向复制过来的新对象,而不是原有的被引用对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import copy


class WorkExperience:
def __init__(self):
self.company = ""
self.time_area = ""


class Resume:
def __init__(self, n):
self.name = n
self.work_experience = WorkExperience()

def set_personal_info(self, s, a):
self.sex = s
self.age = a

def set_work_experience(self, c, ta):
self.work_experience.company = c
self.work_experience.time_area = ta

def print_resume(self):
print(self.name + "," + self.sex + "," + self.age + "," \
+ self.work_experience.company + ":" + self.work_experience.time_area)

def clone(self):
new_resume = copy.deepcopy(self)
new_resume.work_experience = copy.deepcopy(self.work_experience)
return new_resume


if __name__ == "__main__":
resume1 = Resume("Bob")
resume1.set_personal_info("M", "24")
resume1.set_work_experience("Google", "2015")
resume2 = resume1
resume2.set_personal_info("F", "22")
resume2.set_work_experience("Twitter", "2015")
resume1.print_resume()
resume2.print_resume()

resume1 = Resume("Bob")
resume1.set_personal_info("M", "24")
resume1.set_work_experience("Google", "2015")
resume2 = copy.copy(resume1)
resume2.set_personal_info("F", "22")
resume2.set_work_experience("Twitter", "2015")
resume1.print_resume()
resume2.print_resume()

resume1 = Resume("Bob")
resume1.set_personal_info("M", "24")
resume1.set_work_experience("Google", "2015")
resume2 = copy.deepcopy(resume1)
resume2.set_personal_info("F", "22")
resume2.set_work_experience("Twitter", "2015")
resume1.print_resume()
resume2.print_resume()

resume1 = Resume("Bob")
resume1.set_personal_info("M", "24")
resume1.set_work_experience("Google", "2015")
resume2 = resume1.clone()
resume2.set_personal_info("F", "22")
resume2.set_work_experience("Twitter", "2015")
resume1.print_resume()
resume2.print_resume()

模板方法

定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。

模板方法是通过把不变行为搬移到超类,去除子类中的重复代码来体现它的优势。

当不变的和可变的行为在方法的子类实现中混合在一起的时候,不变的行为就会在子类中重复出现。我们通过模板方法模式把这些行为搬移到单一的地方,这样就帮助子类摆脱重复的不变行为的纠缠。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import abc


# 试卷模板
class ExamPaper(object):
__metaclass__ = abc.ABCMeta

def question1(self):
print("question1:" + self.answer1())

@abc.abstractmethod
def answer1(self):
return ""

def question2(self):
print("question2:" + self.answer2())

@abc.abstractmethod
def answer2(self):
return ""


# 学生答题
class ExamPaperA(ExamPaper):
def answer1(self):
return "a"

def answer2(self):
return "a"


class ExamPaperB(ExamPaper):
def answer1(self):
return "b"

def answer2(self):
return "b"


# 客户端
if __name__ == "__main__":
test_paper = ExamPaperA()
print("Paper A:")
test_paper.question1()
test_paper.question2()

print("Paper B:")
test_paper = ExamPaperB()
test_paper.question1()
test_paper.question2()

迪米特法则

如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用,如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。

类之间的耦合越弱,越有利于复用,一个处在弱耦合的类被修改,不会对有关系的类造成波及。

外观模式(Facade)

为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

投资基金代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
class Stock1:
def buy(self):
print("buy stock1")

def sell(self):
print("sell stock1")


class Stock2:
def buy(self):
print("buy stock2")

def sell(self):
print("sell stock2")


class Reality1:
def buy(self):
print("buy reality1")

def sell(self):
print("sell reality1")


# 基金经理
class Fund:
def __init__(self):
self.stock1 = Stock1()
self.stock2 = Stock2()
self.reality1 = Reality1()

def buy(self):
self.stock1.buy()
self.stock2.buy()
self.reality1.buy()

def sell(self):
self.stock1.sell()
self.stock2.sell()
self.reality1.sell()


if __name__ == "__main__":
fund = Fund()
fund.buy()
fund.sell()

何时使用外观模式?这要分三个阶段来说。

  • 首先,在设计初期阶段,应该要有意识的将不同的两个层分离,比如经典的三层架构,就需要考虑在数据访问层和业务逻辑层、业务逻辑层和表示层的 层与层之间建立外观 Facade,这样可以为复杂的子系统提供一个简单的接口,使得耦合大大降低。
  • 其次,在开发阶段,子系统往往因为不断的重构演化而变得越来越复杂,大多数的模式使用时也都会产生很多很小的类,这本是好事,但也给外部调用它们的用户程序带来了使用上的困难,增加外观 Facade 可以提供一个简单的接口,减少它们之间的依赖。
  • 第三,在维护一个遗留的大型系统时,可能这个系统已经非常难以维护和扩展了,但因为它包含非常重要的功能,新的需求开发必须要依赖于它。此时用外观模式 Facade 也是非常 合适的。你可以为新系统开发一个外观 Facade 类,来提供设计粗糙或高度复杂的遗留代码的比较清晰 简单的接口,让新系统与 Facade 对象交互,Facade 与遗留代码交互所有复杂的工作。

建造者模式(Builder)

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

建造者模式,又叫生成器模式。建造者模式可以将一个产品的内部表象与产品的生成过程分割开来,从而可以使一个建造过程生成具有不同的内部表象的产品对象。如果我们用了建造者模式,那么用户就只需指定需要建造的类型就可以得到它们,而具体建造的过程和细节就不需知道了。

建造者模式的好处就是使得建造代码与表示代码分离,由于建造者隐藏了该产品是如何组装的,所以若需要改变一个产品的内部表示,只需要再定义一个具体的建造者就可以了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

import abc

# 产品类,有多个部件
class Pen:
"""class pen"""

class Graphics:
"""class graphics"""


# 抽象建造者类
class PersonBuilder:
__metaclass__ = abc.ABCMeta

def __init__(self, p, g): # 添加产品部件
self.pen = p
self.grahics = g

@abc.abstractmethod
def BuildHead(self):
"""build head"""

@abc.abstractmethod
def BuildBody(self):
"""build body"""


# 具体建造者类
class PersonThinBuilder(PersonBuilder):
def BuildHead(self):
print("build thin head")

def BuildBody(self):
print("build thin body")


class PersonFatBuilder(PersonBuilder):
def BuildHead(self):
print("build fat head")

def BuildBody(self):
print("build fat body")


# 指挥者类
class PersonDirector:
def __init__(self, pb):
self.person_builder = pb

def create_person(self): # 用来指挥建造过程
self.person_builder.BuildHead()
self.person_builder.BuildBody()


if __name__ == "__main__":
# build thin person
person_builder = PersonThinBuilder(Pen(), Graphics())
director = PersonDirector(person_builder)
director.create_person() # 指挥者用 PersonThinBuilder 的方法来建造产品

# build fat person
person_builder = PersonFatBuilder(Pen(), Graphics()) # only replace PersonThinBuilder with PersonFatBuilder
director = PersonDirector(person_builder)
director.create_person() # 指挥者用 PersonFatBuilder 的方法来建造产品

所以说,建造者模式是在当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时适用的模式。

观察者模式 Observer

观察者模式又叫做发布 - 订阅(Publish / Subscribe)模式。

观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import abc


# 抽象观察者,在得到通知时更新自己
class Observer:
__metaclass__ = abc.ABCMeta

def __init__(self, n, nr):
self.name = n
self.notifier = nr

# 更新方法
@abc.abstractmethod
def update(self):
"""updated by notifier"""

# 具体观察者
class NbaObserver(Observer):
def update(self):
print(self.name + "," + self.notifier.state + ", close NBA")


class StockObserver(Observer):
def update(self):
print(self.name + "," + self.notifier.state + ", close stock")


# 抽象通知者,把所有对观察者对象的引用保存在一个聚集里,每个主题都可以有任何数量的观察者。
class Notifier:
def __init__(self):
self.observers = []
self.state = ""

# 增加观察者
def attach(self, o):
self.observers.append(o)

# 移除观察者
def detach(self, o):
self.observers.remove(o)

# 通知
def notify(self):
for observer in self.observers:
observer.update()


# 具体通知者,保存具体观察者对象,在状态改变时,给所有登记过的观察者发出通知。
class Boss(Notifier):
pass


class Secretary(Notifier):
pass


if __name__ == "__main__":
boss = Boss()
nba_observer = NbaObserver("Bob", boss)
boss.attach(nba_observer)
stock_observer = StockObserver("Alice", boss)
boss.attach(stock_observer)
boss.state = "boss's back himself"
boss.notify()

观察者模式特点

将一个系统分割成一系列相互协作的类有一个很不好的副作用,那就是需要维护相关对象间的一致性。我们不希望为了维持一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不便。

而观察者模式的关键对象是主题 Subject 和观察者 Observer,一个 Subject 可以有任意数目的依赖它的 Observer,一旦 Subject 的状态发生了改变,所有的 Observer 都可以得到通知。Subject 发出通知时并不需要知道谁是它的观察者,也就是说,具体观察者是谁,它根本不需要知道。而任何一个具体观察者不知道也不需要知道其他观察者的存在。

什么时候考虑使用观察者模式呢

  • 当一个对象的改变需要同时改变其他对象的时候。而日它不知道具体有多少对象有待改变时,应该考虑使用观察者模式。
  • 当一个抽象模型有两个方面,其中一方面依赖于另一方面,这时用观察者模式可以将这两者封装在独立的对象中使它们各自独立地改变和复用。

总的来说,观容者模式所做的工作其实就是在解除耦合。让耦合的双方都依赖抽象,而不具依赖干且体从而使得各自的变化都不会影响另一边的变化。

抽象工厂模式

抽象工厂模式的优点与缺点:

最大的好处便是易于交换产品系列,由于具体工厂类在一个应用中只需要在初始化的时候出现一次,这就使得改变一个应用的具体工厂变得非常容易,它只需要改变具体工厂即可使用不同的产品配置。我们的设计不能去防止需求的更改,那么我们的理想便是让改动变得最小,现在如果你要更改数据库访问,我们只需要更改具体工厂就可以做到。

第二大好处是,它让具体的创建实例过程与客户端分离,客户端是通过它们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户代码中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import abc


class User:
"""class user"""


class IUser:
__metaclass__ = abc.ABCMeta

@abc.abstractmethod
def insert_user(self, user):
"""insert user"""

@abc.abstractmethod
def get_user(self):
"""get user"""


class SqlserverUser(IUser):
def insert_user(self, user):
print("Insert user into Sqlserver")

def get_user(self):
print("Get user from Sqlserver")


class AccessUser(IUser):
def insert_user(self, user):
print("Insert user into Access")

def get_user(self):
print("Get user from Access")


class Department:
"""class department"""


class IDepartment:
__metaclass__ = abc.ABCMeta

@abc.abstractmethod
def insert_department(self, department):
"""insert department"""

def get_department(self):
"""get department"""


class SqlserverDepartment(IDepartment):
def insert_department(self, department):
print("Insert department into Sqlserver")

def get_department(self):
print("Get department from Sqlserver")


class AccessDepartment(IDepartment):
def insert_department(self, department):
print("Insert department into Access")

def get_department(self):
print("Get department from Access")


class IFactory:
__metaclass__ = abc.ABCMeta

@abc.abstractmethod
def create_user(self):
"""create user"""

@abc.abstractmethod
def create_department(self):
"""create deparment"""


class SqlserverFactory(IFactory):
def create_user(self):
return SqlserverUser()

def create_department(self):
return SqlserverDepartment()


class AccessFactory(IFactory):
def create_user(self):
return AccessUser()

def create_department(self):
return AccessDepartment()


if __name__ == "__main__":
# use sqlserver to insert/get user/department
i_factory = SqlserverFactory()
i_user = i_factory.create_user()
i_user.insert_user(User())
i_user.get_user()
i_department = i_factory.create_department()
i_department.insert_department(Department())
i_department.get_department()

# use access to insert/get user/department
i_factory = AccessFactory() # just replace SqlserverFactory with AccessFactory
i_user = i_factory.create_user()
i_user.insert_user(User())
i_user.get_user()
i_department = i_factory.create_department()
i_department.insert_department(Department())
i_department.get_department()

如果我们应用反射+抽象工厂模式,可以解决数据库访问时的可维护、可扩展的问题。

从这个角度上说,所有在用简单工厂的地方,都可以考虑用反射技术来去除 switchif,解除分支判断带来的耦合。switch 或者 if 是程序里的好东西,但在应对变化上,却显得老态龙钟。反射技术的确可以很好地解决它们难以应对变化,难以维护和扩展的诟病。