初步了解设计模式

Posted 琪齐

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了初步了解设计模式相关的知识,希望对你有一定的参考价值。

一、设计模式介绍

  1、设计模式

设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、

保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。

项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周围不断重复发生的问题,

以及该问题的核心解决方案,这也是它能被广泛应用的原因。

二、设计模式分类

  1、创建型模式

前面讲过,社会化的分工越来越细,自然在软件设计方面也是如此,因此对象的创建和对象的使用分开也就成为了必然趋势。因为对象的创建会消耗掉系统的很多资源,所以单独对对象的创建进行研究,从而能够高效地创建对象就是创建型模式要探讨的问题。这里有6个具体的创建型模式可供研究,它们分别是:

简单工厂模式(Simple Factory);

工厂方法模式(Factory Method);

抽象工厂模式(Abstract Factory);

创建者模式(Builder);

原型模式(Prototype);

单例模式(Singleton)。

说明:严格来说,简单工厂模式不是GoF总结出来的23种设计模式之一。

  2 、结构型模式

在解决了对象的创建问题之后,对象的组成以及对象之间的依赖关系就成了开发人员关注的焦点,因为如何设计对象的结构、继承和依赖关系会影响到后续程序的维护性、代码的健壮性、耦合性等。对象结构的设计很容易体现出设计人员水平的高低,这里有7个具体的结构型模式可供研究,它们分别是:

外观模式(Facade);

适配器模式(Adapter);

代理模式(Proxy);

装饰模式(Decorator);

桥模式(Bridge);

组合模式(Composite);

享元模式(Flyweight)

  3、行为型模式

在对象的结构和对象的创建问题都解决了之后,就剩下对象的行为问题了,如果对象的行为设计的好,那么对象的行为就会更清晰,它们之间的协作效率就会提高,这里有11个具体的行为型模式可供研究,它们分别是:

模板方法模式(Template Method);

观察者模式(Observer);

状态模式(State);

策略模式(Strategy);

职责链模式(Chain of Responsibility);

命令模式(Command);

访问者模式(Visitor);

调停者模式(Mediator);

备忘录模式(Memento);

迭代器模式(Iterator);

解释器模式(Interpreter)。

三、设计模式的六大原则

  1、开放封闭原则

开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:

为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。

  2、里氏代换原则

里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。

LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。

里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,

所以里氏代换原则是对实现抽象化的具体步骤的规范。

  3、依赖倒置原则

这个是开闭原则的基础,具体内容:是对接口编程,依赖于抽象而不依赖于具体。

  4、接口隔离原则

这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,

从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。

  5、迪米特法则(最少知道原则)

 一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。

  6、合成复用原则

 原则是尽量使用合成/聚合的方式,而不是使用继承。

四、设计模式

  1、工厂模式

工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

意图:

定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method 使一个类的实例化延迟到其子类。 

适用性:

当一个类不知道它所必须创建的对象的类的时候。

当一个类希望由它的子类来指定它所创建的对象的时候。

当类将创建对象的职责委托给多个子类中的某一个。

  简单工厂模式

class ShapeFactory(object):
    \'\'\'工厂类\'\'\'

    def getShape(self):
      return self.shape_name

class Circle(ShapeFactory):

  def __init__(self):
    self.shape_name = "Circle"
  def draw(self):
    print(\'draw circle\')

class Rectangle(ShapeFactory):
  def __init__(self):
    self.shape_name = "Retangle"
  def draw(self):
    print(\'draw Rectangle\')

class Shape(object):
  \'\'\'接口类,负责决定创建哪个ShapeFactory的子类\'\'\'
  def create(self, shape):
    if shape == \'Circle\':
      return Circle()
    elif shape == \'Rectangle\':
      return Rectangle()
    else:
      return None


fac = Shape()
obj = fac.create(\'Circle\')
obj.draw()
obj.getShape()

简单工厂
简单工厂

优点:客户端不需要修改代码。

缺点: 当需要增加新的运算类的时候,不仅需新加运算类,还要修改工厂类,违反了开闭原则。

  工厂方法模式

这个和简单工厂有区别,简单工厂模式只有一个工厂,工厂方法模式对每一个产品都有相应的工厂

  好处:增加一个运算类(例如N次方类),只需要增加运算类和相对应的工厂,两个类,不需要修改工厂类。

  缺点:增加运算类,会修改客户端代码,工厂方法只是把简单工厂的内部逻辑判断移到了客户端进行。

class ShapeFactory(object):
    \'\'\'工厂类\'\'\'

    def getShape(self):
        return self.shape_name

class Circle(ShapeFactory):

    def __init__(self):
        self.shape_name = "Circle"
    def draw(self):
        print(\'draw circle\')

class Rectangle(ShapeFactory):
    def __init__(self):
        self.shape_name = "Retangle"

    def draw(self):
        print(\'draw Rectangle\')


class ShapeInterfaceFactory(object):
    \'\'\'接口基类\'\'\'
    def create(self):
        \'\'\'把要创建的工厂对象装配进来\'\'\'
        raise  NotImplementedError

class ShapeCircle(ShapeInterfaceFactory):
    def create(self):
        return Circle()


class ShapeRectangle(ShapeInterfaceFactory):
    def create(self):
        return Rectangle()


shape_interface = ShapeCircle()
obj = shape_interface.create()
obj.getShape()
obj.draw()

shape_interface2 = ShapeRectangle()
obj2 = shape_interface2.create()
obj2.draw()
工厂模式

 

  抽象工厂模式

每一个模式都是针对一定问题的解决方案。抽象工厂模式与工厂方法模式的最大区别就在于,工厂方法模式针对的是一个产品等级结构;

而抽象工厂模式则需要面对多个产品等级结构。

  抽象工厂模式结构

抽象工厂模式是对象的创建模式,它是工厂方法模式的进一步推广。

通过使用抽象工厂模式,可以处理具有相同(或者相似)等级结构中的多个产品族中的产品对象的创建问题

由于这两个产品族的等级结构相同,因此使用同一个工厂族也可以处理这两个产品族的创建问题,这就是抽象工厂模式。

抽象工厂的功能是为一系列相关对象或相互依赖的对象创建一个接口。一定要注意,这个接口内的方法不是任意堆砌的,而是一系列相关或相互依赖的方法。

比如上面例子中的主板和CPU,都是为了组装一台电脑的相关对象。不同的装机方案,代表一种具体的电脑系列。

  使用抽象工厂模式

  1.一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有形态的工厂模式都是重要的。

  2.这个系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。

  3.同属于同一个产品族的产品是在一起使用的,这一约束必须在系统的设计中体现出来。(比如:Intel主板必须使用Intel CPU、Intel芯片组)

  4.系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于实现。

抽象工厂模式的优点

  • 分离接口和实现

  客户端使用抽象工厂来创建需要的对象,而客户端根本就不知道具体的实现是谁,客户端只是面向产品的接口编程而已。也就是说,客户端从具体的产品实现中解耦。

  • 使切换产品族变得容易

  因为一个具体的工厂实现代表的是一个产品族,比如上面例子的从Intel系列到AMD系列只需要切换一下具体工厂。

抽象工厂模式的缺点

  • 不太容易扩展新的产品

  如果需要给整个产品族添加一个新的产品,那么就需要修改抽象工厂,这样就会导致修改所有的工厂实现类。

class AbstractFactory(object):
    computer_name = \'\'
    def createCpu(self):
        pass
    def createMainboard(self):
        pass
 
 
class IntelFactory(AbstractFactory):
    computer_name = \'Intel I7-series computer \'
    def createCpu(self):
        return IntelCpu(\'I7-6500\')
 
    def createMainboard(self):
        return IntelMainBoard(\'Intel-6000\')
 
class AmdFactory(AbstractFactory):
    computer_name = \'Amd 4 computer \'
 
    def createCpu(self):
        return AmdCpu(\'amd444\')
 
    def createMainboard(self):
        return AmdMainBoard(\'AMD-4000\')
 
 
class AbstractCpu(object):
    series_name = \'\'
    instructions = \'\'
    arch=\'\'
 
class IntelCpu(AbstractCpu):
    def __init__(self,series):
        self.series_name = series
 
class IntelCpu(AbstractCpu):
    def __init__(self,series):
        self.series_name = series
 
 
class AmdCpu(AbstractCpu):
    def __init__(self,series):
        self.series_name = series
 
 
class AbstractMainboard(object):
    series_name = \'\'
 
class IntelMainBoard(AbstractMainboard):
    def __init__(self,series):
        self.series_name = series
 
class AmdMainBoard(AbstractMainboard):
    def __init__(self,series):
        self.series_name = series
 
 
 
class ComputerEngineer(object):
 
    def makeComputer(self,computer_obj):
 
        self.prepareHardwares(computer_obj)
 
    def prepareHardwares(self,computer_obj):
        self.cpu = computer_obj.createCpu()
        self.mainboard = computer_obj.createMainboard()
 
        info = \'\'\'------- computer [%s] info:
    cpu: %s
    mainboard: %s
 
 -------- End --------
        \'\'\'% (computer_obj.computer_name,self.cpu.series_name,self.mainboard.series_name)
        print(info)
if __name__ == "__main__":
    engineer = ComputerEngineer()
 
    computer_factory = IntelFactory()
    engineer.makeComputer(computer_factory)
 
    computer_factory2 = AmdFactory()
    engineer.makeComputer(computer_factory2)
抽象工厂

 

  2、建造者模式

意图:

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

适用性:

当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。

当构造过程必须允许被构造的对象有不同的表示时。

  

#建造者模式
 
#相关模式:思路和模板方法模式很像,模板方法是封装算法流程,对某些细节,提供接口由子类修改,建造者模式更为高层一点,将所有细节都交由子类实现。
# 建造者模式:将一个复杂对象的构建与他的表示分离,使得同样的构建过程可以创建不同的表示。
# 基本思想
# 某类产品的构建由很多复杂组件组成;
# 这些组件中的某些细节不同,构建出的产品表象会略有不同;
# 通过一个指挥者按照产品的创建步骤来一步步执行产品的创建;
# 当需要创建不同的产品时,只需要派生一个具体的建造者,重写相应的组件构建方法即可。
 
def printInfo(info):
    print(info)
 
#建造者基类
class PersonBuilder():
    def BuildHead(self):
        pass
 
    def BuildBody(self):
        pass
 
    def BuildArm(self):
        pass
 
    def BuildLeg(self):
        pass
 
#胖子
class PersonFatBuilder(PersonBuilder):
    type = \'胖子\'
    def BuildHead(self):
        printInfo("构建%s的头" % self.type)
 
    def BuildBody(self):
        printInfo("构建%s的身体" % self.type)
 
    def BuildArm(self):
        printInfo("构建%s的手" % self.type)
 
    def BuildLeg(self):
        printInfo("构建%s的脚" % self.type)
 
 
#瘦子
class PersonThinBuilder(PersonBuilder):
    type = \'瘦子\'
    def BuildHead(self):
        printInfo("构建%s的头" % self.type)
 
    def BuildBody(self):
        printInfo("构建%s的身体" % self.type)
 
    def BuildArm(self):
        printInfo("构建%s的手" % self.type)
 
    def BuildLeg(self):
        printInfo("构建%s的脚" % self.type)
 
#指挥者
class PersonDirector():
    pb = None;
    def __init__(self, pb):
        self.pb = pb
 
    def CreatePereson(self):
        self.pb.BuildHead()
        self.pb.BuildBody()
        self.pb.BuildArm()
        self.pb.BuildLeg()
 
def clientUI():
    pb = PersonThinBuilder()
    pd = PersonDirector(pb)
    pd.CreatePereson()
 
    pb = PersonFatBuilder()
    pd = PersonDirector(pb)
    pd.CreatePereson()
    return
 
 
if __name__ == \'__main__\':
    clientUI();
建造者模式

 

  3、单例模式

意图: 

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

适用性:

当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。

当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。
#实现__new__方法
#并在将一个类的实例绑定到类变量_instance上,
#如果cls._instance为None说明该类还没有实例化过,实例化该类,并返回
#如果cls._instance不为None,直接返回cls._instance
class Singleton(object):
    def __new__(cls, *args, **kwargs):
        if not hasattr(cls,\'_instance\'):
            orig = super(Singleton,cls)
            cls._instance =  orig.__new__(cls)
        return cls._instance
 
class MyClass(Singleton):
    def __init__(self,name):
        self.name = name
 
 
a = MyClass("Alex")
b = MyClass("Jack")
 
 
print(a.name)
print(b.name)
单例模式

  4、适配器模式

意图

将一个类的接口转换成客户希望的另外一个接口。Adapter 模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。 
适用性:

你想使用一个已经存在的类,而它的接口不符合你的需求。

你想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作
# 适配器模式
# 将一个类的接口转换成客户希望的另外一个接口。使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
# 应用场景:希望复用一些现存的类,但是接口又与复用环境要求不一致。


def printInfo(info):
    print(info)


# 球员类
class Player():
    name = \'\'

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

    def Attack(self,name):
        pass

    def Defense(self):
        pass


# 前锋
class Forwards(Player):
    def __init__(self,name):
        Player.__init__(self,name)

    def Attack(self):
        printInfo("前锋%s 进攻" % self.name)

    def Defense(self,name):
        printInfo("前锋%s 防守" % self.name)


# 中锋(目标类)
class Center(Player):
   def __init__(self,name):
       Player.__init__(self,name)

   def Attack(self):
       printInfo("中锋%s 进攻" % self.name)

   def Defense(self):
       printInfo("中锋%s 防守" % self.name)


# 后卫
class Guards(Player):
   def __init__(self,name):
       Player.__init__(self,name)

   def Attack(self):
       printInfo("后卫%s 进攻" % self.name)

   def Defense(self):
       printInfo("后卫%s 防守" % self.name)


# 外籍中锋(待适配类)
# 中锋
class ForeignCenter(Player):
    name = \'\'

    def __init__(self,name):
        Player.__init__(self,name)

    def ForeignAttack(self):
        printInfo("外籍中锋%s 进攻" % self.name)

    def ForeignDefense(self):
        printInfo("外籍中锋%s 防守" % self.name)


# 翻译(适配类)
class Translator(Player):
    foreignCenter = None

    def __init__(self,name):
        self.foreignCenter = ForeignCenter(name)

    def Attack(self):
        self.foreignCenter.ForeignAttack()

    def Defense(self):
        self.foreignCenter.ForeignDefense()


def clientUI():
    b = Forwards(\'巴蒂尔\')
    m = Guards(\'姚明\')
    ym = Translator(\'麦克格雷迪\')

    b.Attack()
    m.Defense()
    ym.Attack()
    ym.Defense()
    return

if __name__ == \'__main__\':
    clientUI()
适配器模式

 

  5、桥接模式

意图:
   将抽象部分与实现部分分离,使它们都可以独立的变化。
                                                                    ——《设计模式》GOF

效果及实现要点:
1.Bridge模式使用“对象间的组合关系”解耦了抽象和实现之间固有的绑定关系,使得抽象和实现可以沿着各自的维度来变化。
2.所谓抽象和实现沿着各自维度的变化,即“子类化”它们,得到各个子类之后,便可以任意它们,从而获得不同路上的不同汽车。
3.Bridge模式有时候类似于多继承方案,但是多继承方案往往违背了类的单一职责原则(即一个类只有一个变化的原因),复用性比较差。Bridge模式是比多继承方案更好的解决方法。
4.Bridge模式的应用一般在“两个非常强的变化维度”,有时候即使有两个变化的维度,但是某个方向的变化维度并不剧烈——换言之两个变化不会导致纵横交错的结果,并不一定要使用Bridge模式。

适用性:
   在以下的情况下应当使用桥梁模式:
1.如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的联系。
2.设计要求实现化角色的任何改变不应当影响客户端,或者说实现化角色的改变对客户端是完全透明的。
3.一个构件有多于一个的抽象化角色和实现化角色,系统需要它们之间进行动态耦合。
4.虽然在系统中使用继承是没有问题的,但是由于抽象化角色和具体化角色需要独立变化,设计要求需要独立管理这两者。
总结:
      Bridge模式是一个非常有用的模式,也非常复杂,它很好的符合了开放-封闭原则和优先使用对象,而不是继承这两个面向对象原则

  

 

class AbstractRoad(object):
    \'\'\'公路基类\'\'\'
    car = None
 
class AbstractCar(object):
    \'\'\'车辆基类\'\'\'
 
    def run(self):
        pass
 
class Street(AbstractRoad):
    \'\'\'市区街道\'\'\'
 
    def run(self):
        self.car.run()
        print("在市区街道上行驶")
 
class SpeedWay(AbstractRoad):
    \'\'\'高速公路\'\'\'
 
    def run(self):
        self.car.run()
        print("在高速公路上行驶")
 
 
class Car(AbstractCar):
    \'\'\'小汽车\'\'\'
    def run(self):
        print("小汽车在")
 
class Bus(AbstractCar):
    \'\'\'公共汽车\'\'\'
    def run(self):
        print("公共汽车在")
 
 
if __name__ == "__main__":
    #小汽车在高速上行驶
    road1 = SpeedWay()
    road1.car = Car()
    road1.run()
 
    #
    road2 = SpeedWay()
    road2.car = Bus()
    road2.run()

应用设计模式:
       桥接模式(Bridge)来做(多维度变化);
       结合上面的例子,增加一个维度"人",不同的人开着不同的汽车在不同的路上行驶(三个维度);
       结合上面增加一个类"人",并重新调用. 

class AbstractRoad(object):
    \'\'\'公路基类\'\'\'
    car = None


class AbstractCar(object):
    \'\'\'车辆基类\'\'\'

    def run(self):
        pass


class Street(AbstractRoad):
    \'\'\'市区街道\'\'\'

    def run(self):
        self.car.run()
        print("在市区街道上行驶")


class SpeedWay(AbstractRoad):
    \'\'\'高速公路\'\'\'

    def run(self):
        self.car.run()
        print("在高速公路上行驶")


class Car(AbstractCar):
    \'\'\'小汽车\'\'\'
    def run(self):
        print("小汽车在")


class Bus(AbstractCar):
    \'\'\'公共汽车\'\'\'
    def run(self):
        print("公共汽车在")


if __name__ == "__main__":
    # 小汽车在高速上行驶
    road1 = SpeedWay()
    road1.car = Car()
    road1.run()

    road2 = SpeedWay()
    road2.car = Bus()
    road2.run()
桥接模式

 

  6、组合模式

意图:

将对象组合成树形结构以表示“部分-整体”的层次结构。C o m p o s i t e 使得用户对单个对象和组合对象的使用具有一致性。 
适用性:

你想表示对象的部分-整体层次结构。

你希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。

  

# 应用组合模式的会员卡消费
#
#     那么我们就根据我们会员卡的消费,来模拟一下组合模式的实现吧!let\'s go!
#
#     首先:
#
#            1.我们的部件有,总店,分店,加盟店!
#
#            2.我们的部件共有的行为是:刷会员卡
#
#            3.部件之间的层次关系,也就是店面的层次关系是,总店下有分店、分店下可以拥有加盟店。
#
# 有了我们这几个必要条件后,我的要求就是目前店面搞活动当我在总店刷卡后,就可以累积相当于在所有下级店面刷卡的积分总额,设计的代码如下


class Store(object):
    \'\'\'店面基类\'\'\'

    # 添加店面
    def add(self, store):
        pass

    # 删除店面
    def remove(self, store):
        pass

    def pay_by_card(self):
        pass


class BranchStore(Store):
    def __init__(self, name):
        self.name = name
        self.my_store_list = []

    def pay_by_card(self):
        print("店面[%s]的积分已累加进该会员卡" % self.name)
        for s in self.my_store_list:
            s.pay_by_card()

    # 添加店面
    def add(self, store):
        self.my_store_list.append(store)

    # 删除店面
    def remove(self, store):
        self.my_store_list.remove(store)


class JoinStore(Store):
    \'\'\'加盟店\'\'\'
    def __init__(self,name):
        self.name = name

    def pay_by_card(self):
        print("店面[%s]的积分已累加进该会员卡" %self.name)

    def add(self,store):
        print("无添加子店权限")

    def remove(self,store):
        print("无删除子店权限")


if __name__ == "__main__":
    store = BranchStore("朝阳总店")
    branch = BranchStore("海滨分店")
    join_branch = JoinStore("昌平加盟1店")
    join_branch2 = JoinStore("昌平加盟2店")

    branch.add(join_branch)
    branch.add(join_branch2)

 

以上是关于初步了解设计模式的主要内容,如果未能解决你的问题,请参考以下文章

初步了解VUE源码

react(初步了解)

初步了解思科路由器

初步了解中文编程工具易语言

接口设计模式内部类的初步了解

rocketmq 消息队列 初步了解