day17-Python设计模式

时间:2019-03-18
本文章向大家介绍day17-Python设计模式,主要包括day17-Python设计模式使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

一.单例模式

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

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

1.使用模块

其实,Python 的模块就是天然的单例模式,因为模块在第一次导入时,会生成 .pyc 文件,当第二次导入时,就会直接加载 .pyc 文件,而不会再次执行模块代码。因此,我们只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。

class Singlen(object):
    def foo(self):
        pass
sing = Singlen()

将上面的代码写入到singlen文件中,使用的时候直接导入对象就可以了。

2.使用装饰器

def Singleton(cls):
    _instance = {} # 创建一个存储对象的字典
    def _singleton(*args, **kargs):
        # 判断对象是否已经被创建
        if cls not in _instance:
            _instance[cls] = cls(*args, **kargs)
        return _instance[cls]
    return _singleton

@Singleton
class TestDemo(object):
    a = 1
    def __init__(self, x=0):
        self.x = x

a1 = TestDemo(2)
print(id(a1))  # 22331520

a2 = TestDemo(3)
print(id(a2))  # 22331520

3.使用类

import time
import threading

class Singleton(object):
    _instance_lock = threading.Lock()

    def __init__(self):
        time.sleep(1)

    @classmethod
    def instance(cls, *args, **kwargs):
        if not hasattr(Singleton, "_instance"):
            with Singleton._instance_lock:
                if not hasattr(Singleton, "_instance"):
                    Singleton._instance = Singleton(*args, **kwargs)
        return Singleton._instance

def task(arg):
    obj = Singleton.instance()
    print(obj)

for i in range(10):
    t = threading.Thread(target=task,args=[i,])
    t.start()

time.sleep(20)
obj = Singleton.instance()
print(obj)

4.基于__new__()方法实现

import threading

class Singleton(object):
    _instance_lock = threading.Lock()
    def __init__(self):
        pass

    def __new__(cls, *args, **kwargs):
        # 判断类是否有_instance属性
        if not hasattr(Singleton, "_instance"):
            with Singleton._instance_lock:
                if not hasattr(Singleton, "_instance"):
                    # 创建实例
                    Singleton._instance = object.__new__(cls)
        return Singleton._instance

obj1 = Singleton()
obj2 = Singleton()
print(obj1,obj2)

def task(arg):
    obj = Singleton()
    print(obj)

for i in range(10):
    t = threading.Thread(target=task,args=[i,])
    t.start()

5.使用元类

  • 类由type创建,创建类时,type的__init__方法自动执行,类() 执行type的 __call__方法(类的__new__方法,类的__init__方法)
  • 对象由类创建,创建对象时,类的__init__方法自动执行,对象()执行类的 __call__ 方法

5.1元类例子

class Foo:
    def __init__(self):
        pass
 
    def __call__(self, *args, **kwargs):
        pass
 
obj = Foo()
# 执行type的 __call__ 方法,调用 Foo类(是type的对象)的 __new__方法,用于创建对象,然后调用 Foo类(是type的对象)的 __init__方法,用于对对象初始化。
 
obj()    # 执行Foo的 __call__ 方法   
5.2 实现单例模式
import threading

class SingletonType(type):
    _instance_lock = threading.Lock()
    def __call__(cls, *args, **kwargs):
        if not hasattr(cls, "_instance"):
            with SingletonType._instance_lock:
                if not hasattr(cls, "_instance"):
                    cls._instance = super(SingletonType,cls).__call__(*args, **kwargs)
        return cls._instance

class Foo(metaclass=SingletonType):
    def __init__(self,name):
        self.name = name


obj1 = Foo('name')
obj2 = Foo('name')
print(obj1,obj2)


二.工厂模式
说道工厂,第一反应是可以生产东西,也就是我们可以通过工厂类创建产品。一个函数,传入需要创建的产品类型,输出的结果是我们需要的类型。
import random
import abc

# 两种类型的课程
class BasicCourse(object):
    """
    基础课程
    """
    def get_labs(self):
        return "basic_course: labs"

    def __str__(self):
        return "BasicCourse"


class ProjectCourse(object):
    """
    项目课
    """
    def get_labs(self):
        return "project_course: labs"

    def __str__(self):
        return "ProjectCourse"


# 两种类型的虚拟机
class LinuxVm(object):
    """
    Linux 虚拟机
    """
    def start(self):
        return "Linux vm running"


class MacVm(object):
    """
    Mac OSX 虚拟机
    """
    def start(self):
        return "Mac OSX vm running"


class Factory(metaclass=abc.ABCMeta):
    """
    抽象工厂类, 现在工厂类不仅能创建课程,还能创建虚拟机了
    """
    @abc.abstractmethod
    def create_course(self):
        pass

    @abc.abstractmethod
    def create_vm(self):
        pass

class BasicCourseLinuxFactory(Factory):
    """
    基础课程工厂类
    """
    def create_course(self):
        return BasicCourse()

    def create_vm(self):
        return LinuxVm()


class ProjectCourseMacFactory(Factory):
    """
    项目课程工厂类
    """
    def create_course(self):
        return ProjectCourse()

    def create_vm(self):
        return MacVm()

def get_factory():
    """
    随机获取一个工厂类
    """
    return random.choice([BasicCourseLinuxFactory, ProjectCourseMacFactory])()


if __name__ == '__main__':
    factory = get_factory()
    course = factory.create_course()
    vm = factory.create_vm()
    print(course.get_labs())
    print(vm.start())


三.策略模式
在现实中,一个问题可能有多种显示,比如一个商场的商品做活动,可能有各种可能,如原价,8折,满100送10快等。策略模式它定义了算法家族,分别封装起来,让他们之间可以相互替换,此模式让算法的变化,不会影响到使用算法的客户.
class CashSuper(object):
    '''
    现金收费抽象类
    '''
    def accept_cash(self,money):
        pass


class CashNormal(CashSuper):
    '''
    正常收费子类
    '''
    def accept_cash(self,money):
        return money


class CashRebate(CashSuper):
    '''
    打折收费子类
    '''
    def __init__(self,discount=1):
       self.discount = discount # 折扣

    def accept_cash(self,money):
        return money * self.discount


class CashReturn(CashSuper):
    '''
    返利收费子类
    '''
    def __init__(self,money_condition=0,money_return=0):
        self.money_condition = money_condition
        self.money_return = money_return

    def accept_cash(self,money):
        if money >= self.money_condition:
            return money - (money / self.money_condition) * self.money_return
        return money


#具体策略类
class Context(object):
    def __init__(self,csuper):
        self.csuper = csuper

    def GetResult(self,money):
        return self.csuper.accept_cash(money)


if __name__ == '__main__':
    money = input("原价: ")
    strategy = {}
    strategy[1] = Context(CashNormal())
    strategy[2] = Context(CashRebate(0.8))
    strategy[3] = Context(CashReturn(100,10))
    mode = int(input("选择折扣方式: 1) 原价 2) 8折 3) 满100减10: ")

    if mode in strategy:
        csuper = strategy[mode]
    else:
        print("不存在的折扣方式")
        csuper = strategy[1]
    print("需要支付: ",csuper.GetResult(money))


四.观察者模式

所谓的观察者模式,就是说当一个对象发生变化时,观察者能及时得到通知并且更新。观察者模式在许多的地方都很有用。定义对象之间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象都得到通知并被自动更新。发生改变的对象称为观察目标,被通知的对象称为观察者 。一个观察目标可以对应多个观察者

import abc

class Subject(object):
    '''
    被观察对象的基类
    '''
    def __init__(self):
        # 观察者列表
        self._observers = []

    def attach(self,observer):
        '''
        z注册一个观察者
        :param observer:
        :return:
        '''
        if observer not in self._observers:
            self._observers.append(observer)

    def detach(self,observer):
        '''
        删除一个观察者
        :param observer:
        :return:
        '''
        try:
            self._observers.remove(observer)
        except ValueError:
            pass


    def notify(self):
        '''
        让所有的观察者执行观察者的更新方法
        :return:
        '''
        for observer in self._observers:
            observer.update(self)



class Course(Subject):
    '''
    课程对象,被观察者
    '''
    def __init__(self):
        super(Course, self).__init__()
        self._message = None

    @property
    def message(self):
        return self._message


    @message.setter
    def message(self,msg):
        '''
        设置message属性
        :param msg:
        :return:
        '''
        self._message = msg
        self.notify()


class Observer(object):
    """
    观察者抽象类
    """
    __metaclass__ = abc.ABCMeta

    @abc.abstractmethod
    def update(self, subject):
        pass


class UserObserver(Observer):
    """
    用户观察者
    """
    def update(self, subject):
        print("User observer: %s" % subject.message)



class OrgObserver(Observer):
    """
    机构观察者
    """

    def update(self, subject):
        print("Organization observer: %s" % subject.message)


if __name__ == '__main__':
    # 初始化一个用户观察者
    user = UserObserver()
    # 初始化一个机构观察者
    org = OrgObserver()

    # 初始化一个课程
    course = Course()
    # 注册观察者
    course.attach(user)
    course.attach(org)

    # 设置course.message,这时观察者会收到通知
    course.message = "two observers"

    # 注销一个观察者
    course.detach(user)
    course.message = "single observer"

在上面的代码中,最重要的就是Subject类了,它实现了观察者模式中大部分功能。作为一个被观察的对象,Subject实现了注册观察者,注销观察者和通知观察者的功能。接着我们基于Subject创建了我们的课程Course类,并且当我们设置Course.message属性时,Course对象会通知到所有观察者。可以看出,观察者模式使被观察的对象(主题)和观察者之间解耦了。

五.命令模式

顾名思义,命令模式就是对命令的封装。所谓封装命令,就是将一系列操作封装到命令类中,并且命令类只需要对外公开一个执行方法execute,调用此命令的对象只需要执行命令的execute方法就可以完成所有的操作。这样调用此命令的对象就和命令具体操作之间解耦了。更进一步,通过命令模式我们可以抽象出调用者,接收者和命令三个对象。调用者就是简单的调用命令,然后将命令发送给接收者,而接收者则接收并执行命令,执行命令的方式也是简单的调用命令的execute方法就可以了。发送者与接收者之间没有直接引用关系,发送请求的对象只需要知道如何发送请求,而不必知道如何完成请求。下面让我们使用 Python 来实现命令模式。

import abc

class VmReceiver(object):
    """
    命令接收者,真正执行命令的地方
    """
    def start(self):
        print("Virtual machine start")

    def stop(self):
        print("Virtual machine stop")


class Command(object):
    """
    命令抽象类
    """
    __metaclass__ = abc.ABCMeta

    @abc.abstractmethod
    def execute(self):
        """
        命令对象对外只提供 execute 方法
        """
        pass


class StartVmCommand(Command):
    """
    开启虚拟机的命令
    """

    def __init__(self, recevier):
        """
        使用一个命令接收者初始化
        """
        self.recevier = recevier

    def execute(self):
        """
        真正执行命令的时候命令接收者开启虚拟机
        """
        self.recevier.start()


class StopVmCommand(Command):
    """
    停止虚拟机的命令
    """

    def __init__(self, recevier):
        """
        使用一个命令接收者初始化
        """
        self.recevier = recevier

    def execute(self):
        """
        真正执行命令的时候命令接收者关闭虚拟机
        """
        self.recevier.stop()


class ClientInvoker(object):
    """
    命令调用者
    """
    def __init__(self, command):
        self.command = command

    def do(self):
        self.command.execute()

if __name__ == '__main__':
    recevier = VmReceiver() # 创建命令接收者对象

    # 开启虚拟机对象的创建
    start_command = StartVmCommand(recevier)
    # 执行命令对象
    client = ClientInvoker(start_command)
    client.do() # 执行命令

    # 停止虚拟机对象的创建
    stop_command = StopVmCommand(recevier)
    client.command = stop_command
    client.do()

六.模板方法模式

提到模板,不难想到文档模板、简历模板等。其实模板方法模式中的模板就是这个意思,在模板方法模式中,我们先定义一个类模板,在这个类中,我们定义了各种操作的顺序(轮毂或者说是骨架),但是并不实现这些操作,这些操作由子类来操作。

import abc

class Eating(object):
    '''
    吃饭模板基类
    '''
    __metaclass__ = abc.ABCMeta

    def eating(self):
        '''
        吃饭方法中,确定要执行哪些操作
        :return:
        '''
        self.xicai()
        self.aoxifan()
        self.chaocai()
        print('开始吃饭!!')

    @abc.abstractmethod
    def xicai(self):
        '''定义洗菜类的抽象基类'''
        pass

    @abc.abstractmethod
    def aoxifan(self):
        '''熬稀饭基类'''
        pass

    @abc.abstractmethod
    def chaocai(self):
        pass


class TomEating(Eating):
    '''
    tom想要吃饭,必须要实现吃饭的步骤
    '''
    def xicai(self):
        print('tom is xicai')

    def aoxifan(self):
        print('tom is aoxifan')

    def chaocai(self):
        print('tom is chaocai')


class JohnEating(Eating):
    '''
    tom想要吃饭,必须要实现吃饭的步骤
    '''
    def xicai(self):
        print('John is xicai')

    def aoxifan(self):
        print('John is aoxifan')

    def chaocai(self):
        print('John is chaocai')

if __name__ == '__main__':
    john = JohnEating()
    john.eating()
    tom = TomEating()
    tom.eating()

七.代理模式

代理模式在生活中比比皆是。比如你通过代理上网,比如你不会去华西牛奶生产地直接买牛奶,而是到超市这个代理购买牛奶,这些例子中都存在着代理模式。所谓代理模式就是给一个对象提供一个代理,并由代理对象控制对原对象的访问。通过代理,我们可以对访问做一些控制。在开发网站的过程中,针对一些频繁访问的资源,可以使用缓存。

from time import sleep

class Redis(object):
    """
    用于模拟 redis 服务
    """

    def __init__(self):
        """
        使用字典存储数据
        """
        self.cache = dict()

    def get(self, key):
        """
        获取数据
        """
        return self.cache.get(key)

    def set(self, key, value):
        """
        设置数据
        """
        self.cache[key] = value


class Image(object):
    """
    图片对象,图片存在七牛云存储中,我们只保存了一个地址
    """
    def __init__(self, name):
        self.name = name

    @property
    def url(self):
        sleep(2)
        return "https://dn-syl-static.qbox.me/img/logo-transparent.png"

class Page(object):
    """
    用于显示图片
    """
    def __init__(self, image):
        """
        需要图片进行初始化
        """
        self.image = image

    def render(self):
        """
        显示图片
        """
        print(self.image.url)

redis = Redis()

class ImageProxy(object):
    """
    图片代理,首次访问会从真正的图片对象中获取地址,以后都从 Redis 缓存中获取
    """
    def __init__(self, image):
        self.image = image

    @property
    def url(self):
        addr = redis.get(self.image.name)
        if not addr:
            addr = self.image.url
            print("Set url in redis cache!")
            redis.set(self.image.name, addr)
        else:
            print("Get url from redis cache!")
        return addr


if __name__ == '__main__':
    img = Image(name="logo")
    proxy = ImageProxy(img)
    page = Page(proxy)
    # 首次访问
    page.render()
    print("")
    # 第二次访问
    page.render()

八.组合模式

什么是组合模式?按照定义来说,组合模式是将对象组合成树形结构表示,使得客户端对单个对象和组合对象的使用具有一致性。组合模式的使用通常会生成一颗对象树,对象树中的叶子结点代表单个对象,其他节点代表组合对象。调用某一组合对象的方法,其实会迭代调用所有其叶子对象的方法。

使用组合模式的经典例子是 Linux 系统内的树形菜单和文件系统。在树形菜单中,每一项菜单可能是一个组合对象,其包含了菜单项和子菜单,这样就形成了一棵对象树。在文件系统中,叶子对象就是文件,而文件夹就是组合对象,文件夹可以包含文件夹和文件,同样又形成了一棵对象树。同样的例子还有员工和领导之间的关系

import abc

class Worker(object):
    """
    工作抽象类
    """
    __metaclass__ = abc.ABCMeta
    def __init__(self, name):
        self.name = name

    @abc.abstractmethod
    def work(self):
        # 工作的抽象基类
        pass


class Employe(Worker):
    """
    员工类
    """
    __metaclass__ = abc.ABCMeta

    def work(self):
        print("Employ: %s start to work " % self.name)

class Leader(Worker):
    """
    领导类
    """
    def __init__(self, name):
        self.members = []
        super(Leader, self).__init__(name)

    def add_member(self, employe):
        if employe not in self.members:
            self.members.append(employe)

    def remove_member(self, employe):
        if employe in self.members:
            self.members.remove(employe)

    def work(self):
        print("Leader: %s start to work" % self.name)
        for employe in self.members:
            employe.work()

if __name__ == '__main__':
    employe_1 = Employe("employe_1")
    employe_2 = Employe("employe_2")
    leader_1 = Leader("leader_1")
    leader_1.add_member(employe_1)
    leader_1.add_member(employe_2)

    employe_3 = Employe("employe_3")
    leader_2 = Leader("leader_2")
    leader_2.add_member(employe_3)
    leader_2.add_member(leader_1)

    leader_2.work()

九.外观模式

所谓外观模式,就是将各种子系统的复杂操作通过外观模式简化,让客户端使用起来更方便简洁。比如你夏天晚上出门时,要关闭电灯,关闭电视机,关闭空调,如果有了一个总开关,通过它可以关闭电灯,电视机和空调,你出门的时候关闭总开关就行了。

class User(object):
    """
    用户类
    """
    def is_login(self):
        return True

    def has_privilege(self, privilege):
        return True


class Course(object):
    """
    课程类
    """
    def can_be_learned(self):
        return True


class Lab(object):
    """
    实验类
    """
    def can_be_started(self):
        return True

class Client(object):
    """
    客户类,用于开始一个实验
    """
    def __init__(self, user, course, lab):
        self.user = user
        self.course = course
        self.lab = lab

    def start_lab(self):
        """
        开始实验,需要一系列的判断:用户是否登陆,课程是否可以学习,实验是否可以开始。判断非常繁琐!
        """
        if self.user.is_login() and self.course.can_be_learned() and self.lab.can_be_started():
            print("start lab")
        else:
            print("can not start lab")


class FacadeLab(object):
    """
    新的Lab类,应用了面向对象模式
    """
    def __init__(self, user, course, lab):
        self.user = user
        self.course = course
        self.lab = lab

    def can_be_started(self):
        if self.user.is_login() and self.course.can_be_learned() and self.lab.can_be_started():
            return True
        else:
            return False


class NewClient(object):
    """
    新的客户类,使用外观模式
    """
    def __init__(self, facade_lab):
        self.lab = facade_lab

    def start_lab(self):
        """
        开始实验,只需要判断 FacadeLab 是否可以开始
        """
        if self.lab.can_be_started:
            print("start lab")
        else