设计模式是什么?

  设计模式是经过总结、优化的,对我们经常会碰到的一些编程问题的可重用解决方案。一个设计模式并不像一个类或一个库那样能够直接作用于我们的代码。反之,设计模式更为高级,它是一种必须在特定情形下实现的一种方法模板。设计模式不会绑定具体的编程语言。一个好的设计模式应该能够用大部分编程语言实现(如果做不到全部的话,具体取决于语言特性)。最为重要的是,设计模式也是一把双刃剑,如果设计模式被用在不恰当的情形下将会造成灾难,进而带来无穷的麻烦。然而如果设计模式在正确的时间被用在正确地地方,它将是你的救星。

  起初,你会认为“模式”就是为了解决一类特定问题而特别想出来的明智之举。说的没错,看起来的确是通过很多人一起工作,从不同的角度看待问题进而形成的一个最通用、最灵活的解决方案。也许这些问题你曾经见过或是曾经解决过,但是你的解决方案很可能没有模式这么完备。

  虽然被称为“设计模式”,但是它们同“设计“领域并非紧密联系。设计模式同传统意义上的分析、设计与实现不同,事实上设计模式将一个完整的理念根植于程序中,所以它可能出现在分析阶段或是更高层的设计阶段。很有趣的是因为设计模式的具体体现是程序代码,因此可能会让你认为它不会在具体实现阶段之前出现(事实上在进入具体实现阶段之前你都没有意识到正在使用具体的设计模式)。

  可以通过程序设计的基本概念来理解模式:增加一个抽象层。抽象一个事物就是隔离任何具体细节,这么做的目的是为了将那些不变的核心部分从其他细节中分离出来。当你发现你程序中的某些部分经常因为某些原因改动,而你不想让这些改动的部分引发其他部分的改动,这时候你就需要思考那些不会变动的设计方法了。这么做不仅会使代码可维护性更高,而且会让代码更易于理解,从而降低开发成本。

设计模式的定义:为了解决面向对象系统中重要和重复的设计封装在一起的一种代码实现框架,可以使得代码更加易于扩展和调用

四个基本要素:模式名称,问题,解决方案,效果

六大原则:

  • 1.开闭原则:一个软件实体,如类,模块和函数应该对扩展开发,对修改关闭.既软件实体应尽量在不修改原有代码的情况下进行扩展.
  • 2.里氏替换原则:所有引用父类的方法必须能透明的使用其子类的对象
  • 3.依赖倒置原则:高层模块不应该依赖底层模块,二者都应该依赖其抽象,抽象不应该依赖于细节,细节应该依赖抽象,换而言之,要针对接口编程而不是针对实现编程
  • 4.接口隔离原则:使用多个专门的接口,而不是使用单一的总接口,即客户端不应该依赖那些并不需要的接口
  • 5.迪米特法则:一个软件实体应该尽可能的少与其他实体相互作用
  • 6.单一直责原则:不要存在多个导致类变更的原因.即一个类只负责一项职责

设计模式的分类

  • 创建型模式(5种):工厂方法模式、抽象工厂模式、创建者模式、原型模式、单例模式
  • 结构型模式(7种):适配器模式、桥模式、组合模式、装饰模式、外观模式、享元模式、代理模式
  • 行为型模式(11种):解释器模式、责任链模式、命令模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、访问者模式、模板方法模式

主流的设计模式

接口

  定义:一种特殊的类,声明了若干方法,要求继承该接口的类必须实现这种方法

    作用:限制继承接口的类的方法的名称及调用方式,隐藏了类的内部实现

# 接口:若干抽象方法的集合
# 作用:限制实现接口的类必须按照接口给定的调用方式实现这些方法;对高层模块隐藏了类的内部实现

from abc import ABCMeta, abstractmethod


# 接口 abstract class
class PayMent(metaclass=ABCMeta):
    @abstractmethod   # 定义抽象方法的关键字
    def pay(self, money):
        pass
    # def pay(self, money):
    #     raise NotImplementedError('must implement pay methods')


class AliPay(PayMent):
    # 子类继承接口,必须实现接口中定义的抽象方法,否则不能实例化对象
    def pay(self, money):
        print(f'支付宝支付{money}元')


class WechatPay(PayMent):
    def pay(self, money):
        print(f'微信支付{money}元')


class ApplePay(PayMent):
    def pay(self, money):
        print(f'苹果支付{money}元')


def finish_pay(p, money):
    p.pay(money)


p1 = AliPay()
p2 = WechatPay()
p3 = ApplePay()
finish_pay(p1, 100)
finish_pay(p2, 200)
finish_pay(p3, 300)
""" 里式替换原则: 所有引用父类的地方必须能透明地使用其子类的对象 """
class User:
    def show_name(self):
        print('我是用户A')
        return '普通用户'


class VipUser(User):
    def show_name(self):
        print('我是VIP用户')
        return 'VIP用户'


def show_user(user):
    # 父类和子类的show_name参数和返回值类型必须相同
    res = user.show_name()
    print(res)


u = User()
show_user(u)

""" 接口隔离原则:使用多个专门的接口,而不是用单一的总接口,即客户端不应该依赖那些他不需要的接口 """
class LandAnimal(metaclass=ABCMeta):
    @abstractmethod
    def walk(self):
        pass


class WaterAnimal(metaclass=ABCMeta):
    @abstractmethod
    def swim(self):
        pass


class SkyAnimal(metaclass=ABCMeta):
    @abstractmethod
    def fly(self):
        pass


class Tiger(LandAnimal):
    def walk(self):
        print("老虎走路")


class Frog(LandAnimal, WaterAnimal):
    pass

一、简单工厂模式

定义: 不直接向客户暴露对象创建的实现细节,而是通过一个工厂类来负责创建产品类的实例

角色:

    • 工厂角色
    • 抽象产品角色
    • 具体产品角色

优点: 隐藏了对象创建代码的细节,客户端不需要修改代码

缺点: 违反了单一职责原则,将创建逻辑集中到一个工厂里面,当要添加新产品时,违背了开闭原则

Python的设计模式

# -*- coding: utf-8 -*-

"""
@Datetime: 2019/4/17
@Author: Zhang Yafei
"""
from abc import ABCMeta, abstractmethod


class Payment(metaclass=ABCMeta):
    """ 抽象产品角色 """
    @abstractmethod
    def pay(self, money):
        pass


class AiliPay(Payment):
    """ 具体产品角色 """
    def __init__(self, enable_yuebao=False):
        self.enable_yuebao = enable_yuebao

    def pay(self, money):
        if self.enable_yuebao:
            print('使用余额宝支付%s元' % money)
        else:
            print('使用支付宝支付%s元' % money)


class ApplePay(Payment):
    """ 具体产品角色 """
    def pay(self, money):
        print('使用苹果支付支付%s元' % money)


class PaymentFactory(object):
    """ 工厂角色 """
    @staticmethod
    def create_payment(method):
        if method == 'alipay':
            return AiliPay()
        elif method == 'yuebao':
            return AiliPay(True)
        elif method == 'applepay':
            return ApplePay()
        else:
            return NameError


p = PaymentFactory()
f = p.create_payment('yuebao')
f.pay(100)

简单工厂模式