Python设计模式-结构型:适配器模式,装饰者模式,代理模式,组合模式,外观模式

Python设计模式-结构型:适配器模式,装饰者模式,代理模式,组合模式,外观模式

  • 适配器模式定义及简单实现案例
  • 装饰者模式定义及简单实现案例
  • 代理模式定义及简单实现案例
  • 组合模式定义及简单实现案例
  • 外观模式定义及简单实现案例

适配器模式 adapter

电子产品的电源插头插在转换插头上,然后转换插头插上电源,电子产品就能正常工作了。这就是适配器模式

# -*- coding: utf-8 -*-
class OldCourse(object):
    """
    老的课程类
    """

    def show(self):
        """
        显示关于本课程的所有信息
        """
        print("show description")
        print("show teacher of course")
        print("show labs")


class Page(object):
    """
    使用课程对象的客户端
    """

    def __init__(self, course):
        self.course = course

    def render(self):
        self.course.show()


class NewCourse(object):
    """
    新的课程类, 为了模块化显示课程信息,实现了新的课程类
    """
    def show_desc(self):
        """
        显示描述信息
        """
        print("show description")

    def show_teacher(self):
        """
        显示老师信息
        """
        print("show teacher of course")

    def show_labs(self):
        """
        显示实验
        """
        print("show labs")


class Adapter(object):
    """
    适配器, 尽管实现了新的课程类,但是在很多代码中还是需要使用 OldCourse.show() 方法
    """

    def __init__(self, course):
        self.course = course

    def show(self):
        """
        适配方法,调用真正的操作
        """
        self.course.show_desc()
        self.course.show_teacher()
        self.course.show_labs()


if __name__ == '__main__':
    old_course = OldCourse()
    page = Page(old_course)
    page.render()
    print("")
    new_course = NewCourse()
    # 新课程类没有 show 方法,我们需要使用适配器进行适配
    adapter = Adapter(new_course)
    page = Page(adapter)
    page.render()

适配器模式就是把一个类的接口变换成客户端所期待的另一种接口,使原本因接口不兼容而无法在一起工作的两个类能够在一起工作。

装饰者模式 Decorator

装饰者模式能动态的给对象添加行为。如果你对 Flask 比较熟悉的话,应该知道在使用 Flask-Login 的时候可以使用 login_required 装饰器包装一个需要用户登录访问的view

# -*- coding: utf-8 -*-
from functools import wraps

HOST_DOCKER = 0


def docker_host_required(f):
    """
    装饰器,必须要求 host 类型是 HOST_DOCKER
    """
    @wraps(f)
    def wrapper(*args, **kwargs):
        if args[0].type != HOST_DOCKER:
            raise Exception("Not docker host")
        else:
            return f(*args, **kwargs)
    return wrapper


class Host(object):
    """
    host 类
    """

    def __init__(self, type):
        self.type = type

    # 装饰这一方法
    @docker_host_required
    def create_container(self):
        print("create container")


if __name__ == '__main__':
    # 初始化 Host
    host = Host(HOST_DOCKER)
    host.create_container()
    print("")
    # 再次初始化 Host
    host = Host(1)
    host.create_container()

在上面的代码中,Host有一个方法Host.create_container,只有当Host实例的类型是DOCKER_HOST的时候才能执行该方法。为了加上这一行为,我们使用了装饰者模式。可以看出使用装饰者模式,我们可以动态改变类的行为,同时能提高代码复用性,因为任何类型为HOST_DOCKER的Host都可以使用该装饰器。另外要说明下:为了更好的实现装饰器,我们使用functools.wrap函数。

代理模式 proxy

所谓代理模式就是给一个对象提供一个代理,并由代理对象控制对原对象的访问。通过代理,我们可以对访问做一些控制。在开发网站的过程中,针对一些频繁访问的资源,我们会使用缓存。

# -*- coding: utf-8 -*-
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()

代理对象和真实的对象之间都实现了共同的接口,这使我们可以在不改变原接口情况下,使用真实对象的地方都可以使用代理对象。其次,代理对象在客户端和真实对象之间直接起到了中介作用,同时通过代理对象,我们可以在将客户请求传递给真实对象之前做一些必要的预处理。

组合模式 composite

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

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

# -*- coding: utf-8 -*-
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()

外观模式 facade

所谓外观模式,就是将各种子系统的复杂操作通过外观模式简化,让客户端使用起来更方便简洁。

  • 比如你夏天晚上出门时,要关闭电灯,关闭电视机,关闭空调,如果有了一个总开关,通过它可以关闭电灯,电视机和空调,你出门的时候关闭总开关就行了。
    • 在这个例子中,你就是客户端,总开关就是外观模式的化身
# -*- coding: utf-8 -*-


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:
            print("can not start lab")


if __name__ == '__main__':
    user = User()
    course = Course()
    lab = Lab()
    client = Client(user, course, lab)
    client.start_lab()

    print("Use Facade Pattern:")
    facade_lab = FacadeLab(user, course, lab)
    facade_client = NewClient(facade_lab)
    facade_client.start_lab()

外观模式的主要目的在于降低系统的复杂程度,在面向对象软件系统中,类与类之间的关系越多,不能表示系统设计得越好,反而表示系统中类之间的耦合度太大,这样的系统在维护和修改时都缺乏灵活性,因为一个类的改动会导致多个类发生变化,而外观模式的引入在很大程度上降低了类与类之间的耦合关系。引入外观模式之后,增加新的子系统或者移除子系统都非常方便,客户类无须进行修改(或者极少的修改),只需要在外观类中增加或移除对子系统的引用即可。

posted @ 2022-07-10 12:36  OCEANEYES.GZY  阅读(137)  评论(0编辑  收藏  举报