作为一个程序员我们都知道设计模式分为三大类,创建型设计模式,行为设计模式,结构型设计模式。
今天带大家见识一下python里面的行为设计模式,并与js做一些比较。
1.策略模式
import types
class StrategyExample:
def __init__(self, func=None):
self.name = 'Strategy Example 0'
if func is not None:
self.execute = types.MethodType(func,self)
def execute(self):
print(self.name)
def execute_replacement1(self):
print(self.name + ' from execute 1')
def execute_replacement2(self):
print(self.name + ' from execute 2')
if __name__ == '__main__':
strat0 = StrategyExample()
strat1 = StrategyExample(execute_replacement1)
strat1.name = 'Strategy Example 1'
strat2 = StrategyExample(execute_replacement2)
strat2.name = 'Strategy Example 2'
strat0.execute()
strat1.execute()
strat2.execute()
### OUTPUT ###
# Strategy Example 0
# Strategy Example 1 from execute 1
# Strategy Example 2 from execute 2
需要注意的是,重新定义excute的方式,如果是前端的同学会奇怪,为什么不这样写:self.execute = func,这和js不一样,这种函数里面需要有self。
既然这样我们看下js如何写策略模式
var strategies={
"S":function (salary) {
return salary * 4;
},
"A":function (salary) {
return salary * 3;
},
"B":function (salary) {
return salary * 2;
}
};
//具体的计算方法
var calculateBonus=function (level, salary) {
return strategies[level](salary);
};
console.log(calculateBonus('S',1000));
console.log(calculateBonus('A',4000));
以上模拟了一个情景,就是年终奖的发放。。。 分为3个级别,年底4薪,3,2.。
我们可以把上面这个js代码改造一下,写成python的策略模式。
class Strategy(object):
"""抽象算法类"""
def bonus(self):
pass
class StrategyA(Strategy):
def bonus(self,salary):
print 'my bonus {}' .format salary*3
class StrategyB(Strategy):
def bobus(self,salary):
print 'my bonus {}' .format salary*2
class Context(object):
"""上下文,作用就是封装策略的实现细节,用户只需要知道有哪些策略可用"""
def __init__(self, level):
# 初始化时传入具体的策略实例
if level == 'A':
self.strategy = StrategyA()
else:
self.strategy = StrategyB()
def excute(self,salary):
# 负责调用具体的策略实例的接口
self.strategy.bonus(salary)
2 观察者模式
class Subject(object):
def __init__(self):
self._observers = []
def attach(self, observer):
if observer not in self._observers:
self._observers.append(observer)
def detach(self, observer):
try:
self._observers.remove(observer)
except ValueError:
pass
def notify(self, modifier=None):
for observer in self._observers:
if modifier != observer:
observer.update(self)
# Example usage
class Data(Subject):
def __init__(self, name=''):
Subject.__init__(self)
self.name = name
self._data = 0
@property
def data(self):
return self._data
@data.setter
def data(self, value):
self._data = value
self.notify()
class HexViewer:
def update(self, subject):
print(u'HexViewer: Subject %s has data 0x%x' %(subject.name, subject.data))
class DecimalViewer:
def update(self, subject):
print(u'DecimalViewer: Subject %s has data %d' %(subject.name, subject.data))
# Example usage...
def main():
data1 = Data('Data 1')
data2 = Data('Data 2')
view1 = DecimalViewer()
view2 = HexViewer()
data1.attach(view1)
data1.attach(view2)
data2.attach(view2)
data2.attach(view1)
print(u"Setting Data 1 = 10")
data1.data = 10
print(u"Setting Data 2 = 15")
data2.data = 15
print(u"Setting Data 1 = 3")
data1.data = 3
print(u"Setting Data 2 = 5")
data2.data = 5
print(u"Detach HexViewer from data1 and data2.")
data1.detach(view2)
data2.detach(view2)
print(u"Setting Data 1 = 10")
data1.data = 10
print(u"Setting Data 2 = 15")
data2.data = 15
if __name__ == '__main__':
main()
### OUTPUT ###
# Setting Data 1 = 10
# DecimalViewer: Subject Data 1 has data 10
# HexViewer: Subject Data 1 has data 0xa
# Setting Data 2 = 15
# HexViewer: Subject Data 2 has data 0xf
# DecimalViewer: Subject Data 2 has data 15
# Setting Data 1 = 3
# DecimalViewer: Subject Data 1 has data 3
# HexViewer: Subject Data 1 has data 0x3
# Setting Data 2 = 5
# HexViewer: Subject Data 2 has data 0x5
# DecimalViewer: Subject Data 2 has data 5
# Detach HexViewer from data1 and data2.
# Setting Data 1 = 10
# DecimalViewer: Subject Data 1 has data 10
# Setting Data 2 = 15
# DecimalViewer: Subject Data 2 has data 15
以下几个知识需要讲解:
1.self._data 是私有属性,需要通过@property注解访问。
2.通过 @data.setter 进行设置,促发观察对象。
感觉python里面的观察者模式代码很少,而且比较容易看清,那我们来看看js是如何做的。
// 首先定义一个可观察对象
var Observable = function() {
// 按类型收集订阅对象
this.subscribers = {};
} ;
// Observable 对象原型上上的3个方法: subscribe, unsubscribe, publish
Observable.prototype = {
constructor: Observable,
// @param1 type @param2 fn
subscribe: function(type, fn) {
// 首先查看对象subscribers上是否存在type属性
if (!this.subscribers[type]) {
this.subscribers[type] = [];
}
// 将订阅者加入到 subscribers 中
this.subscribers[type].push(fn);
},
// unsubscribe 取消订阅 @param1 type @param2 fn
unsubscribe: function(type, fn) {
// 先判断subscribers中存不存在type这个属性,不存在直接返回
if (!this.subscribers[type]) {
return;
}
// 存在type,将要取消订阅的订阅者找出,从订阅者名单中删除掉
var listeners = this.subscribers[type],
i,
len = listeners.length;
for (i = 0; i < len; i++) {
if (listeners[i] === fn) {
// 将取消订阅的观察者observer移除
listeners.splice(i, 1);
return;
}
}
},
// publish: 发布 @param1 type @param2 eventArgs(事件信息)
publish: function(type, event) {
// 判断观察者对象集合subscribers中存不存在type属性,不存在则表示为订阅,直接返回
if (!this.subscribers[type]) {
return;
}
// 先判断对象event中存不存在type这个属性,不存在就创建该属性
if (!event[type]) {
event[type] = type;
}
// 找到该事件对应的观察者,并发布通知
var listeners = this.subscribers[type],
i,
len = listeners.length;
for (i = 0; i < len; i++) {
listeners[i](event);
}
}
}
举个例子看一下:
// 创建一个可观察者Observable实例
var publisher = new Observable();
// 创建一个对象传入到订阅者中
var eventArgs = {message: "hello observer pattern!"};
// 创建一个订阅者
var subscriber = function(eventArgs) {
console.log(eventArgs.message);
};
// 订阅 @param1 type @param2 fn
publisher.subscribe("message", subscriber);
// 发布 @param1 type @param2 eventArgs(事件信息)
publisher.publish("message", eventArgs); // "hello observer pattern!"
我们试想一下如何在python中实现像jquery里面的链式调用呢。
class Person(object):
def __init__(self,name,actipn,play):
self.name = name
self.action = action
self.play = play
def do_action(self):
print(self.name, self.action.name, end=' ')
self.action.change(self)
return self.action
def do_play(self):
print(self.name, self.action.name, end=' ')
self.action.change(self)
return self.action
class Action(object):
def __init__(self, name ,target = None):
self.name = name
self.target = target
def amount(self, val):
print(val, end=' ')
return self
def change(self,qq):
self.target = qq
def stop(self):
print('then stop')
return self.target
class Play(object):
def __init__(self, name):
self.name = name
self.target = ''
def play(self, val):
print(val, end=' ')
return self
def change(self, qq):
self.target = qq
def stop(self):
print('then stop')
return self.target
if __name__ == '__main__':
move = Action('move')
play = Play('qwer')
person = Person('Jack', move ,play)
aa = person.do_action().amount('5m').stop().do_play().play('ddd')
output:// Jack move 5m then stop
Jack qwer ddd
讲了一个题外话,我们最后看一下单例模式:
3 单例模式
单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。
下面我们来看一个经典的单例模式:
class Singleton(object):
_instance = None
def __new__(cls, *args, **kw):
if not cls._instance:
cls._instance = super(Singleton, cls).__new__(cls, *args, **kw)
return cls._instance
class MyClass(Singleton):
a = 1
>>> one = MyClass()
>>> two = MyClass()
>>> one == two
True
>>> one is two
True
>>> id(one), id(two)
(4303862608, 4303862608)
在上面的代码中,我们将类的实例和一个类变量 _instance 关联起来,如果 cls._instance 为 None 则创建实例,否则直接返回 cls._instance。
我们也可以用装饰器来实现
from functools import wraps
def singleton(cls):
instances = {}
@wraps(cls)
def getinstance(*args, **kw):
if cls not in instances:
instances[cls] = cls(*args, **kw)
return instances[cls]
return getinstance
@singleton
class MyClass(object):
def __init__(self, name):
self.name = name
one = MyClass('ddd')
two = MyClass('eee')
print one.name
print two.name
print one == two
print one is two
print (id(one), id(two))
上面的打印内容:
ddd
ddd
True
True
(139659475230160, 139659475230160)
不好意思,我有需要在这里插播一段广告,因为作为一个精通前后短的人,我必须要装个逼。
Python在定义变量的时候不用指明具体的的类型,解释器会在运行的时候会自动检查变量的类型,并根据需要进行隐式的类型转化。因为Python是动态语言,所以一般情况下是不推荐进行类型转化的。比如"+"操作时,如果加号两边是数据就进行加法操作,如果两边是字符串就进行字符串连接操作,如果两边是列表就进行合并操作,甚至可以进行复数的运算。解释器会在运行时根据两边的变量的类型调用不同的内部方法。当加号两边的变量类型不一样的时候,又不能进行类型转化,就会抛出TypeError的异常。
我们在js里面相加,类型不同也会自动换,而python却不一样。用js实现单例模式可没有那么复杂
var Singleton = (function () {
var instantiated;
function init() {
/*这里定义单例代码*/
return {
publicMethod: function () {
console.log('hello world');
},
publicProperty: 'test'
};
}
return {
getInstance: function () {
if (!instantiated) {
instantiated = init();
}
return instantiated;
}
};
})();
上面运用到了闭包的知识点,其实有一个更为简洁的方法。
function Universe() {
// 判断是否存在实例
if (typeof Universe.instance === 'object') {
return Universe.instance;
}
// 其它内容
this.start_time = 0;
this.bang = "Big";
// 缓存
Universe.instance = this;
// 隐式返回this
}
var uni = new Universe();
var uni2 = new Universe();
console.log(uni === uni2); // true
最后我们再来看一下如何使用元类写python的单例模式
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
# Python2
class MyClass(object):
__metaclass__ = Singleton
# Python3
# class MyClass(metaclass=Singleton):
# pass
之前在魔术方法里面讲过call 的用法,大家可以参考。
4 元类
什么是元类,之前已经提到过,这个东西在python里面有点华而不实,所以我先贴上代码
class dog(object):
pass
class animal(object):
pass
class ModelMetaclass(type):
def __new__(cls, name, bases, attrs):
print name
print(cls)
print(bases)
def log(self,aa):
print aa
attrs['func'] = log
if not attrs.has_key('key'):
attrs['key'] = 'ffff'
return type.__new__(cls, name, bases, attrs)
// python2 元类使用方法
class Model2(dog,animal):
__metaclass__ = ModelMetaclass
pass
print Model2().key
print dir(Model2())
print Model2().func('777777')
打印如下
print name ==== 'Model2'
print cls ==== <class '__main__.ModelMetaclass'>
print bases ==== (<class '__main__.dog'>, <class '__main__.animal'>)
print Model2().key ==== ffffff
print dir(Model2()) ===== [....,'func' , 'key']
print Model2().func('777777') ===== 777777
现在知道元类是干嘛的了把,它可以让指向该元类的类实例获取某些属性和方法,相当于修饰了该类。
cls是指的元类本身,bases是值得集成的类,name就是该类本身的类名。
好了,今天就到这里,下回继续为大家讲解。