目录
工厂方法模式见Task02
一、建造者模式
问题描述
我的要求是你用程序画一个小人,这在游戏程序里非常常见,现在简单一点,要求是小人要有头、身体、两手、两脚就可以了。
问题分析
这里建造小人的’过程’是稳定的,都需要头身手脚,而具体建造的’细节’是不同的,有胖有瘦有高有矮。如果你需要将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示的意图时,我们需要应用于一个设计模式,‘建造者模式(Builder)’,又叫生成器模式。
模式定义
建造者模式(Builder),将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
它主要用于创建一些复杂的对象,这些对象内部子对象的建造顺序通常是稳定的,但每个子对象本身的构建通常面临着复杂的变化。
代码实现
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D
from matplotlib.patches import Ellipse
class PersonBuilder(object):
def __init__(self, ax, color):
self.ax = ax
self.color = color
def build_head(self):
pass
def build_body(self):
pass
def build_arm_left(self):
pass
def build_arm_right(self):
pass
def build_leg_left(self):
pass
def build_leg_right(self):
pass
class PersonFatBuilder(PersonBuilder):
def build_head(self):
self.ax.add_patch(Ellipse(xy=(50 + 15, -20 - 15), width=30, height=30, color=self.color))
def build_body(self):
self.ax.add_patch(Ellipse(xy=(45 + 20, -50 - 25), width=40, height=50, color=self.color))
def build_arm_left(self):
self.ax.add_patch(Line2D(xdata=(50, 30), ydata=(-50, -100), color=self.color))
def build_arm_right(self):
self.ax.add_patch(Line2D(xdata=(80, 100), ydata=(-50, -100), color=self.color))
def build_leg_left(self):
self.ax.add_patch(Line2D(xdata=(60, 45), ydata=(-100, -150), color=self.color))
def build_leg_right(self):
self.ax.add_patch(Line2D(xdata=(70, 85), ydata=(-100, -150), color=self.color))
class PersonThinBuilder(PersonBuilder):
def build_head(self):
self.ax.add_patch(Ellipse(xy=(50 + 15, -20 - 15), width=30, height=30, color=self.color))
def build_body(self):
self.ax.add_patch(Ellipse(xy=(60 + 5, -50 - 50), width=10, height=50, color=self.color))
def build_arm_left(self):
self.ax.add_patch(Line2D(xdata=(60, 40), ydata=(-50, -100), color=self.color))
def build_arm_right(self):
self.ax.add_patch(Line2D(xdata=(70, 90), ydata=(-50, -100), color=self.color))
def build_leg_left(self):
self.ax.add_patch(Line2D(xdata=(60, 45), ydata=(-100, -150), color=self.color))
def build_leg_right(self):
self.ax.add_patch(Line2D(xdata=(70, 85), ydata=(-100, -150), color=self.color))
# 指挥者
class PersonDirector(object):
def __init__(self, pb):
self.pb = pb
def create_person(self):
self.pb.build_head()
self.pb.build_body()
self.pb.build_arm_left()
self.pb.build_arm_right()
self.pb.build_leg_left()
self.pb.build_leg_right()
plt.xlim((0, 150))
plt.ylim((-150, 0))
plt.axis("off")
plt.show()
if __name__ == '__main__':
gThin = plt.figure(figsize=(8, 8))
axThin = gThin.add_subplot(1, 1, 1)
ptb = PersonThinBuilder(axThin, "k")
pdThin = PersonDirector(ptb)
pdThin.create_person()
gFat = plt.figure(figsize=(8, 8))
axFat = gFat.add_subplot(1, 1, 1)
pfb = PersonFatBuilder(axFat, "k")
pdFat = PersonDirector(pfb)
pdFat.create_person()
二、观察者模式
问题描述
我们设想一个场景,假设公司几个同事日常喜欢工作摸鱼,有看 NBA 的,有炒股的,还有玩儿游戏的。但摸鱼肯定怕被自己领导或老板发现,怎么办呢?他们只好每隔几分钟就起来看看老板或者自己的领导有没有回来。这很不方便,想象一下,如果有几十个同事在摸鱼(这公司怕是要倒闭了),大家总不能跟赶集一样,隔几分钟就起来走几步吧。
此时,有人突拍大腿:为啥不整个吹哨人呢,比如前台小妹子?如果老板来了,她只需给我们发个微信消息通知一下就行了呀——有道理!先建个群把大家都拉进去,有领导来了就让前台小妹发消息。这下方便了,只要消息来了就知道是领导来了,赶紧采取行动。而且,如果这会儿突然不想摸鱼了,那把消息屏蔽了就行,明天又想摸鱼,把消息屏蔽取消就可以了。皆大欢喜。
问题分析
我们用观察者模式重新分析开始的问题,针对领导的群就是 Subject,员工则是 Observer,一个 Subject 可以有多个 Observer,它不需要关心到底有哪些 Observer,Observer 之间也不需要知道彼此存在。当 Subject 的状态发生变化(即领导回来)时,所有的 Observer 都会得到通知,并更新自己的行为(努力工作)。
当然,反过来一个 Observer 可以订阅多个 Subject,任意一个 Subject 的状态发生变化,该 Observer 都会得到通知。这样就既解决了一致性问题,又不会过紧耦合。
模式定义
观察者模式又叫作发布-订阅(Publish/Subscribe)模式。
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
- Subject类,可翻译为主题或抽象通知者,一般用一个抽象类或者一个接口实现。它把所有对观察者对象的引用保存在一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。
- Observer类,抽象观察者,为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。这个接口叫作更新接口。抽象观察者一般用一个抽象类或者一个接口实现。更新接口通常包含一个update()方法,这个方法叫作更新方法。
- ConcreteSubject类,叫作具体主题或具体通知者,将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色通常用一个具体子类实现。
- ConcreteObserver类,具体观察者,实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。具体观察者角色可以保存一个指向具体主题对象的引用。具体观察者角色通常用一个具体子类实现。
代码实现
class Observer(object):
def __init__(self, name, sub):
self.name = name
self.sub = sub
def update(self):
pass
class Subject(object):
subjectState = ""
def attach(self, observer: Observer):
pass
def detach(self, observer: Observer):
pass
def notify(self):
pass
class StockObserver(Observer):
def __init__(self, name, sub: Subject):
super().__init__(name, sub)
def update(self):
print(" ".join([self.sub.subjectState, self.name, "关闭股票行情,继续工作!"]))
class NBAObserver(Observer):
def __init__(self, name, sub: Subject):
super().__init__(name, sub)
def update(self):
print(" ".join([self.sub.subjectState, self.name, "关闭NBA直播,继续工作!"]))
class Boss(Subject):
__observers = []
__action = ""
def attach(self, observer):
self.__observers.append(observer)
def detach(self, observer):
self.__observers.remove(observer)
def notify(self):
for each in self.__observers:
each.update()
def get_action(self):
return self.__action
def set_action(self, action):
self.__action = action
subjectState = property(get_action, set_action)
class Secretary(Subject):
__observers = []
__action = ""
def attach(self, observer):
self.__observers.append(observer)
def detach(self, observer):
self.__observers.remove(observer)
def notify(self):
for each in self.__observers:
each.update()
def get_action(self):
return self.__action
def set_action(self, action):
self.__action = action
subjectState = property(get_action, set_action)
if __name__ == '__main__':
huhansan = Boss()
coworker1 = StockObserver("魏关姹", huhansan)
coworker2 = NBAObserver("易管查", huhansan)
huhansan.attach(coworker1)
huhansan.attach(coworker2)
huhansan.detach(coworker1)
# 老板回来
huhansan.subjectState = "我胡汉三回来了!"
# 发出通知
huhansan.notify()
三、原型模式
问题描述
要求有一个简历类,必须要有姓名,可以设置性别和年龄,可以设置工作经历。最终我需要写三份简历。
问题分析
一般在初始化的信息不发生变化的情况下,克隆是最好的办法。这既隐藏了对象创建的细节,又对性能是大大的提高。
- 浅复制:被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象。
- 深复制:深复制把引用对象的变量指向复制过的新对象,而不是原有的被引用的对象。
模式定义
原型模式(Prototype),用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。
原型模式其实就是从一个对象再创建另外一个可定制的对象,而且不需要知道任何创建的细节。
它不用重新初始化对象,而是动态地获得对象运行时的状态。
代码实现
import copy
class Prototype(object):
def __init__(self):
self._objects = {}
def register(self, name, obj):
self._objects[name] = obj
def unregister(self, name):
if name in self._objects:
del self._objects[name]
def clone(self, name, **kwargs):
prototypeObj = self._objects.get(name)
if not prototypeObj:
raise ValueError("Incorrect object name")
obj = copy.deepcopy(prototypeObj)
obj.__dict__.update(kwargs)
return obj
class workExperience(object):
__workDate = 0
__company = 0
def get_workDate(self):
return self.__workDate
def set_workDate(self, workDate):
self.__workDate = workDate
def get_company(self):
return self.__company
def set_company(self, company):
self.__company = company
workDate = property(get_workDate, set_workDate)
company = property(get_company, set_company)
class Resume(object):
def __init__(self, name):
self.name = name
self.work = workExperience()
def setPersonalInfo(self, sex, age):
self.sex = sex
self.age = age
def setWorkExperience(self, timeArea, company):
self.work.workDate = timeArea
self.work.company = company
def display(self):
print(" ".join([self.name, self.sex, self.age]))
print(" ".join(["工作经历:", self.work.workDate, self.work.company]))
if __name__ == '__main__':
prototype = Prototype()
a = Resume('大鸟')
a.setPersonalInfo('男', '29')
a.setWorkExperience('1998-2000', 'XX公司')
prototype.register('a', a)
b = prototype.clone('a')
b.setWorkExperience('1998-2006', 'YY企业')
c = prototype.clone('a')
c.setPersonalInfo('男', '24')
c.setWorkExperience('1998-2003', 'ZZ企业')
a.display()
b.display()
c.display()
四、模板方法模式
问题描述
题目抄错了,那就不是考试题目了,而考试试卷最大的好处就是,大家都是一样的题目,特别是标准化的考试,比如全是选择或判断的题目,那就最大化地限制了答题者的发挥,大家都是ABCD或打钩打叉,非对即错的结果。这其实就是一个典型的设计模式。
问题分析
既然用了继承,并且肯定这个继承有意义,就应该要成为子类的模板,所有重复的代码都应该要上升到父类去,而不是让每个子类都去重复。
模板方法登场了,当我们要完成在某一细节层次一致的一个过程或一系列步骤,但其个别步骤在更详细的层次上的实现可能不同时,我们通常考虑用模板方法模式来处理。
模式定义
模板方法(Template Method)模式,定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
模板方法模式是通过把不变行为搬移到超类,去除子类中的重复代码来体现它的优势。
当不变的和可变的行为在方法的子类实现中混合在一起的时候,不变的行为就会在子类中重复出现。我们通过模板方法模式把这些行为搬移到单一的地方,这样就帮助子类摆脱重复的不变行为的纠缠。
代码实现
# 金庸小说考题试卷
class TestPaper(object):
def testQuestion1(self):
print("杨过得到,后来给了郭靖,炼成倚天剑、屠龙刀的玄铁可能是[ ] a.球墨铸铁 b.马口铁 c.高速合金钢 d.碳素纤维")
print("答案:" + self.Answer1())
def testQuestion2(self):
print("杨过、程英、陆无双铲除了情花,造成[ ] a.使这种植物不再害人 b.使一种珍稀物种灭绝了 c.破坏了那个生物圈的生态平衡 d.造成该地区沙漠化")
print("答案:" + self.Answer2())
def testQuestion3(self):
print("蓝凤凰致使华山师徒、桃谷六仙呕吐不止,如果你是大夫,会给他们开什么药[ ] a.阿司匹林 b.牛黄解毒片 c.氟哌酸 d.让他们喝大量的生牛奶 e.以上全不对")
print("答案:" + self.Answer3())
def Answer1(self):
return ""
def Answer2(self):
return ""
def Answer3(self):
return ""
class TestPaperA(TestPaper):
def Answer1(self):
return "b"
def Answer2(self):
return "c"
def Answer3(self):
return "a"
class TestPaperB(TestPaper):
def Answer1(self):
return "c"
def Answer2(self):
return "a"
def Answer3(self):
return "a"
if __name__ == '__main__':
print("学生甲抄的试卷:")
studentA = TestPaperA()
studentA.testQuestion1()
studentA.testQuestion2()
studentA.testQuestion3()
print("学生乙抄的试卷:")
studentB = TestPaperB()
studentB.testQuestion1()
studentB.testQuestion2()
studentB.testQuestion3()