Python工程之设计模式总结
Python之23种设计模式
目录
- 设计模式介绍
- GoF该书设计了23个设计模式
- 设计模式(Design Patterns)——可复用面向对象软件的基础
- 设计模式分类
- 1 创建型模式
- 2 结构型模式
- 3 行为型模式
- 设计模式6大原则
- 实战示例
- 创建型
- 0. 简单工厂模式(Simple Factory)
- 1.工厂方法模式(Factory Method)
- 2.抽象工厂模式(Abstract Factory)
- 3.创建者模式(Builder)
- 4.原型模式(Prototype)
- 5.单例模式(Singleton)
- 结构型
- 6.适配器模式(Adapter)
- 7.代理模式(Proxy)
- 8.装饰模式(Decorator)
- 9.桥模式(Bridge、多维度)
- 10.组合模式(Composite)
- 11.外观模式(Facade)
- 12.享元模式(Flyweight)
- 行为型
- 13.观察者模式(Observer)
- 14.状态模式(State)
- 15.策略模式(Strategy)
- 16.职责链模式(Chain of Responsibility)
- 17.命令模式(Command)
- 18.访问者模式(Visitor)
- 19.调停者模式(Mediator)
- 20.备忘录模式(Memento)
- 21. 迭代器模式(Iterator)
- 22. 解释器模式(Interpreter)
- 23.模板方法(Template Method)
设计模式介绍
GoF该书设计了23个设计模式
Python一切皆对象解释:我们用到的每个数字和字符串还是集合还是..本质都有相应的类对应如:name = “axc”可以通过点进行调用:name.strip()
设计模式(Design Patterns)——可复用面向对象软件的基础
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被广泛应用的原因。
设计模式分类
经典的《设计模式》一书归纳出23种设计模式,这23种模式又可归为,创建型、结构型和行为型3大类
1 创建型模式
前面讲过,社会化的分工越来越细,自然在软件设计方面也是如此,因此对象的创建和对象的使用分开也就成为了必然趋势。因为对象的创建会消耗掉系统的很多资源,所以单独对对象的创建进行研究,从而能够高效地创建对象就是创建型模式要探讨的问题。这里有6个具体的创建型模式可供研究,它们分别是:
0. 简单工厂模式(Simple Factory) #说明:严格来说,简单工厂模式不是GoF总结出来的23种设计模式之一。
1.工厂方法模式(Factory Method)
2.抽象工厂模式(Abstract Factory)
3.创建者模式(Builder)
4.原型模式(Prototype)
5.单例模式(Singleton)
2 结构型模式
在解决了对象的创建问题之后,对象的组成以及对象之间的依赖关系就成了开发人员关注的焦点,因为如何设计对象的结构、继承和依赖关系会影响到后续程序的维护性、代码的健壮性、耦合性等。对象结构的设计很容易体现出设计人员水平的高低,这里有7个具体的结构型模式可供研究,它们分别是:
6.适配器模式(Adapter)
7.代理模式(Proxy)
8.装饰模式(Decorator)
9.桥模式(Bridge、多维度)
10.组合模式(Composite)
11.外观模式
12.享元模式(Flyweight)
3 行为型模式
在对象的结构和对象的创建问题都解决了之后,就剩下对象的行为问题了,如果对象的行为设计的好,那么对象的行为就会更清晰,它们之间的协作效率就会提高,这里有11个具体的行为型模式可供研究,它们分别是:
13.观察者模式(Observer) #典型的发布订阅
14.状态模式(State)
15.策略模式(Strategy)
16.职责链模式(Chain of Responsibility)
17.命令模式(Command)
18.访问者模式(Visitor)
19.调停者模式(Mediator)
20.备忘录模式(Memento) #如:虚拟机快照 #没讲(克隆:深copy、快照:浅copy)
21. 迭代器模式(Iterator)
22. 解释器模式(Interpreter)
23.模板方法(Template Method)
设计模式6大原则
1、开闭原则(Open Close Principle)
开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。
2、里氏代换原则(Liskov Substitution Principle)
里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。—— From Baidu 百科
3、依赖倒转原则(Dependence Inversion Principle)
这个是开闭原则的基础,具体内容:是对接口编程,依赖于抽象而不依赖于具体。
4、接口隔离原则(Interface Segregation Principle)
这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。
5、迪米特法则(最少知道原则)(Demeter Principle)
为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。
6、合成复用原则(Composite Reuse Principle)
原则是尽量使用合成/聚合的方式,而不是使用继承。
实战示例
创建型
0. 简单工厂模式(Simple Factory)
说明:严格来说,简单工厂模式不是GoF总结出来的23种设计模式之一。
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
意图:
定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method 使一个类的实例化延迟到其子类。
适用性:
当一个类不知道它所必须创建的对象的类的时候。
当一个类希望由它的子类来指定它所创建的对象的时候。
当类将创建对象的职责委托给多个子类中的某一个。
示例代码:
- __author__ = "Burgess Zheng"
- #!/usr/bin/env python
- #-*- coding:utf-8 -*-
- '''
- simple Factory Method
- '''
- class Shape(object):
- '''
- 父类
- '''
- def draw(self):
- raise NotImplementedError
- class Circle(Shape):
- '''
- Shape子类
- '''
- def draw(self):
- print('draw circle')
- class Rectangle(Shape):
- '''
- Shape的子类
- '''
- def draw(self):
- print('draw Rectangle')
- class ShapeFactory(object):
- '''
- 工厂模式:暴露给用户去调用的,
- 用户可通过该类进行选择Shape的子类进行实例化
- '''
- def create(self, shape):
- if shape == 'Circle':
- return Circle()
- elif shape == 'Rectangle':
- return Rectangle()
- else:
- return None
- fac = ShapeFactory() #实例化工厂类
- obj = fac.create('Circle') #实例化Shape的Circle子类
- obj.draw()
执行结果:
Shape(父类 or 基类):提取出所有子类的重复方法代码
Circle(Shape子类 or 派生类):作用为画圆形
Rectangle(Shape子类 or 派生类):作用为画矩形
ShapeFactory(新式类):该类作用为用户可根据该类对象创建指定的Shape子类对象(Circle or Rectangle)
优点:客户端不需要修改代码。
缺点: 当需要增加新的运算类的时候,不仅需新加运算类,还要修改工厂类,违反了开闭原则。
1.工厂方法模式(Factory Method)
这个和简单工厂有区别,简单工厂模式只有一个工厂,工厂方法模式对每一个产品都有相应的工厂
好处:增加一个运算类(例如N次方类),只需要增加运算类和相对应的工厂,两个类,不需要修改工厂类。
缺点:增加运算类,会修改客户端代码,工厂方法只是把简单工厂的内部逻辑判断移到了客户端进行
示例代码:
- __author__ = "Burgess Zheng"
- #!/usr/bin/env python
- #-*- coding:utf-8 -*-
- '''
- Factory Method
- '''
- '''
- 工厂方法模式是简单工厂模式的衍生,解决了许多简单工厂模式的问题。
- 首先完全实现‘开-闭 原则’,实现了可扩展。其次更复杂的层次结构,可以应用于产品结果复杂的场合。
- 工厂方法模式的对简单工厂模式进行了抽象。有一个抽象的Factory类(可以是抽象类和接口),这个类将不在负责具体的产品生产,而是只制定一些规范,具体的生产工作由其子类去完成。在这个模式中,工厂类和产品类往往可以依次对应。即一个抽象工厂对应一个抽象产品,一个具体工厂对应一个具体产品,这个具体的工厂就负责生产对应的产品。
- 工厂方法模式(Factory Method pattern)是最典型的模板方法模式(Templete Method pattern)应用。
- '''
- 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()
执行结果:
ShapeFactory(父类 or 基类):提取出所有子类的重复方法代码
Circle(Shape子类 or 派生类):作用为画圆形
Rectangle(Shape子类 or 派生类):作用为画矩形
ShapeInterfaceFactory(父类 or 基类):提取出所有子类的重复方法代码
ShapeCircle(ShapeInterfaceFactory的子类 or 派生类):作用为创建指定的Circle对象
ShapeRectangle(ShapeInterfaceFactory的子类 or 派生类):作用为创建指定的Rectangle对象
2.抽象工厂模式(Abstract Factory)
每一个模式都是针对一定问题的解决方案。抽象工厂模式与工厂方法模式的最大区别就在于,工厂方法模式针对的是一个产品等级结构;而抽象工厂模式则需要面对多个产品等级结构。
在学习抽象工厂具体实例之前,应该明白两个重要的概念:产品族和产品等级。
所谓产品族,是指位于不同产品等级结构中,功能相关联的产品组成的家族。比如AMD的主板、芯片组、CPU组成一个家族,Intel的主板、芯片组、CPU组成一个家族。而这两个家族都来自于三个产品等级:主板、芯片组、CPU。一个等级结构是由相同的结构的产品组成,示意图如下:
显然,每一个产品族中含有产品的数目,与产品等级结构的数目是相等的。产品的等级结构与产品族将产品按照不同方向划分,形成一个二维的坐标系。横轴表示产品的等级结构,纵轴表示产品族,上图共有两个产品族,分布于三个不同的产品等级结构中。只要指明一个产品所处的产品族以及它所属的等级结构,就可以唯一的确定这个产品。
上面所给出的三个不同的等级结构具有平行的结构。因此,如果采用工厂方法模式,就势必要使用三个独立的工厂等级结构来对付这三个产品等级结构。由于这三个产品等级结构的相似性,会导致三个平行的工厂等级结构。随着产品等级结构的数目的增加,工厂方法模式所给出的工厂等级结构的数目也会随之增加。如下图:
那么,是否可以使用同一个工厂等级结构来对付这些相同或者极为相似的产品等级结构呢?当然可以的,而且这就是抽象工厂模式的好处。同一个工厂等级结构负责三个不同产品等级结构中的产品对象的创建。
可以看出,一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象。显然,这时候抽象工厂模式比简单工厂模式、工厂方法模式更有效率。对应于每一个产品族都有一个具体工厂。而每一个具体工厂负责创建属于同一个产品族,但是分属于不同等级结构的产品。
抽象工厂模式结构
抽象工厂模式是对象的创建模式,它是工厂方法模式的进一步推广。
假设一个子系统需要一些产品对象,而这些产品又属于一个以上的产品等级结构。那么为了将消费这些产品对象的责任和创建这些产品对象的责任分割开来,可以引进抽象工厂模式。这样的话,消费产品的一方不需要直接参与产品的创建工作,而只需要向一个公用的工厂接口请求所需要的产品。
通过使用抽象工厂模式,可以处理具有相同(或者相似)等级结构中的多个产品族中的产品对象的创建问题。如下图所示:
由于这两个产品族的等级结构相同,因此使用同一个工厂族也可以处理这两个产品族的创建问题,这就是抽象工厂模式。
根据产品角色的结构图,就不难给出工厂角色的结构设计图。
可以看出,每一个工厂角色都有两个工厂方法,分别负责创建分属不同产品等级结构的产品对象。
抽象工厂的功能是为一系列相关对象或相互依赖的对象创建一个接口。一定要注意,这个接口内的方法不是任意堆砌的,而是一系列相关或相互依赖的方法。比如上面例子中的主板和CPU,都是为了组装一台电脑的相关对象。不同的装机方案,代表一种具体的电脑系列。
由于抽象工厂定义的一系列对象通常是相关或相互依赖的,这些产品对象就构成了一个产品族,也就是抽象工厂定义了一个产品族。
这就带来非常大的灵活性,切换产品族的时候,只要提供不同的抽象工厂实现就可以了,也就是说现在是以一个产品族作为一个整体被切换。
在什么情况下应当使用抽象工厂模式
1.一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有形态的工厂模式都是重要的。
2.这个系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。
3.同属于同一个产品族的产品是在一起使用的,这一约束必须在系统的设计中体现出来。(比如:Intel主板必须使用Intel CPU、Intel芯片组)
4.系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于实现。
抽象工厂模式的起源
抽象工厂模式的起源或者最早的应用,是用于创建分属于不同操作系统的视窗构建。比如:命令按键(Button)与文字框(Text)都是视窗构建,在UNIX操作系统的视窗环境和Windows操作系统的视窗环境中,这两个构建有不同的本地实现,它们的细节有所不同。
在每一个操作系统中,都有一个视窗构建组成的构建家族。在这里就是Button和Text组成的产品族。而每一个视窗构件都构成自己的等级结构,由一个抽象角色给出抽象的功能描述,而由具体子类给出不同操作系统下的具体实现。
抽象工厂模式的优点
- 分离接口和实现
客户端使用抽象工厂来创建需要的对象,而客户端根本就不知道具体的实现是谁,客户端只是面向产品的接口编程而已。也就是说,客户端从具体的产品实现中解耦。
- 使切换产品族变得容易
因为一个具体的工厂实现代表的是一个产品族,比如上面例子的从Intel系列到AMD系列只需要切换一下具体工厂。
抽象工厂模式的缺点
- 不太容易扩展新的产品
如果需要给整个产品族添加一个新的产品,那么就需要修改抽象工厂,这样就会导致修改所有的工厂实现类。
示例代码:
- __author__ = "Burgess Zheng"
- #!/usr/bin/env python
- #-*- coding:utf-8 -*-
- '''
- Abstract Factory
- '''
- 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 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,factory_obj):
- self.prepareHardwares(factory_obj)
- def prepareHardwares(self,factory_obj):
- self.cpu = factory_obj.createCpu()
- self.mainboard = factory_obj.createMainboard()
- info = '''------- computer [%s] info:
- cpu: %s
- mainboard: %s
- -------- End --------
- '''% (factory_obj.computer_name,self.cpu.series_name,self.mainboard.series_name)
- print(info)
- if __name__ == "__main__":
- engineer = ComputerEngineer() #装机工程师
- intel_factory = IntelFactory() #intel工厂
- engineer.makeComputer(intel_factory)
- amd_factory = AmdFactory() #adm工厂
- engineer.makeComputer(amd_factory)
执行结果:
AbstractFactory(父类or基类 )
IntelFactory(AbstractFactory的子类or派生类):作用为进行了创建自定品牌的零件
AmdFactory(AbstractFactory的子类or派生类):作用为进行了创建自定品牌的零件
AbstractCpu(父类or基类 )
IntelCpu(AbstractCpu的子类or派生类):作用为记录cup的型号
AmdCpu(AbstractCpu的子类or派生类):作用为记录cup的型号
AbstractMainboard(父类or基类 )
IntelMainBoard(AbstractMainboard的子类or派生类):作用为记录主板的型号
AmdMainBoard(AbstractMainboard的子类or派生类):作用为记录主板的型号
ComputerEngineer(新式类):作用为根据工厂对象(如IntelFactory())让其组装自身型号的零件
抽象工厂和工厂模式的对比区别:
抽象工厂:规定死了,依赖限制,假上面实验,你用intel的机器只能配置intel的CPU不能配置AMD的CPU(由各自的工厂指定自己的产品生产品牌)
工厂模式:不是固定死的,举例:你可使用intel的机器配置AMD的CPU
3.创建者模式(Builder)
意图:
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
适用性:
当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
当构造过程必须允许被构造的对象有不同的表示时。
示例代码:
- __author__ = "Burgess Zheng"
- #!/usr/bin/env python
- #-*- coding:utf-8 -*-
- '''
- Builder
- '''
- #建造者模式
- #相关模式:思路和模板方法模式很像,模板方法是封装算法流程,对某些细节,提供接口由子类修改,建造者模式更为高层一点,将所有细节都交由子类实现。
- # 建造者模式:将一个复杂对象的构建与他的表示分离,使得同样的构建过程可以创建不同的表示。
- # 基本思想
- # 某类产品的构建由很多复杂组件组成;
- # 这些组件中的某些细节不同,构建出的产品表象会略有不同;
- # 通过一个指挥者按照产品的创建步骤来一步步执行产品的创建;
- # 当需要创建不同的产品时,只需要派生一个具体的建造者,重写相应的组件构建方法即可。
- 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()
- pb2 = PersonFatBuilder()
- #pd = PersonDirector(pb)
- pd.pb = pb2
- pd.CreatePereson()
- return
- if __name__ == '__main__':
- clientUI();
执行结果:
personBuilder(父类 or 基类)
PersonFatBuilder(personBuilder的子类 or派生类):作用为创建一个胖子的身体部位
PersonThinBuilder(personBuilder的子类 or派生类):作用为创建一个瘦子的身体部位
PersonDirector(新式类):作用为根据personBuilder子类对象(如PersonFatBuilder())让其创建其身体部位
4.原型模式(Prototype)
意图:
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
适用性:
当要实例化的类是在运行时刻指定时,例如,通过动态装载;或者为了避免创建一个与产品类层次平行的工厂类层次时;或者当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。
示例代码:
- __author__ = "Burgess Zheng"
- #!/usr/bin/env python
- #-*- coding:utf-8 -*-
- '''
- Prototype
- '''
- import copy
- class Prototype:
- def __init__(self):
- self._objects = {}
- def register_object(self, name, obj):
- """Register an object"""
- self._objects[name] = obj
- def unregister_object(self, name):
- """Unregister an object"""
- del self._objects[name]
- def clone(self, name, **attr):
- """Clone a registered object and update inner attributes dictionary"""
- obj = copy.deepcopy(self._objects.get(name))
- obj.__dict__.update(attr)
- return obj
- def main():
- class A:
- def __str__(self):
- return "I am A"
- a = A()
- prototype = Prototype()
- prototype.register_object('a', a)
- b = prototype.clone('a', a=1, b=2, c=3)
- print(a)
- print(b.__dict__)
- print(b.a, b.b, b.c)
- if __name__ == '__main__':
- main()
执行结果:
通过执行结果我们可以看出:通过一个类的方法复制a对象,进行a对象的变量赋值
5.单例模式(Singleton)
意图:
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
适用性:
当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。
当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。
示例代码:
- __author__ = "Burgess Zheng"
- #!/usr/bin/env python
- #-*- coding:utf-8 -*-
- '''
- Singleton
- '''
- # 实现__new__方法
- # 并在将一个类的实例绑定到类变量_instance上,
- # 如果cls._instance为None说明该类还没有实例化过,实例化该类,并返回
- # 如果cls._instance不为None,直接返回cls._instance
- class Singleton(object):
- def __new__(cls, *args, **kwargs):
- if not hasattr(cls, '_instance'):
- # cls = a = MyClass('Burgess')
- # 判断是否有a该实例存在,前面是否已经有人实例过,如果内存没有该实例...往下执行
- # 需要注明该父类的内存空间内最多允许相同名字子类的实例对象存在1个(不可多个)
- orig = super(Singleton, cls) # farther class
- cls._instance = orig.__new__(cls)
- # orig =让cls继承指定的父类 Singleton
- # cls._instance = 创建了MyClass('Burgess') 该实例
- # 这两句相当于外面的 a= MyClass('Burgess')
- return cls._instance # 具体的实例
- class MyClass(Singleton):
- def __init__(self, name):
- self.name = name
- class Nana(Singleton):
- def __init__(self, name):
- self.name = name
- a = MyClass("Burgess")
- print(a.name)
- b = MyClass("Crystal")
- print(a.name)
- print(b.name)
- b.name = 'xx'
- print(a.name)
- print(b.name)
执行结果:
通过执行结果我们可以看出:一个类永远只允许一个实例化对象,不管多少个进行实例化,都返回第一个实例化的对象
结构型
6.适配器模式(Adapter)
意图:
将一个类的接口转换成客户希望的另外一个接口。Adapter 模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
适用性:
你想使用一个已经存在的类,而它的接口不符合你的需求。
你想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作。
(仅适用于对象Adapter )你想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。
示例代码:
- __author__ = "Burgess Zheng"
- #!/usr/bin/env python
- #-*- coding:utf-8 -*-
- '''
- 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):
- 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('巴蒂尔')
- ym = Guards('姚明')
- m = Translator('麦克格雷迪')
- b.Attack()
- m.Defense()
- ym.Attack()
- b.Defense()
- return
- if __name__ == '__main__':
- clientUI()
执行结果:
Player:(父类or基类)
国内
Forwards(Player的子类or派生类):作用为国内球员的动作方法
Center(Player的子类or派生类):作用为国内球员的动作方法
Guards(Player的子类or派生类):作用为国内球员的动作方法
国外:
ForeignCenter(Player的子类or派生类):作用为国外球员的动作方法(动作虽然一样但是动作方法的名字和国内动作方法的名字不一样)
Translator(Player的子类or派生类):作用为适配器,国内球员的动作方法的名字一样(但是方法内调用了国外球员对象的动作方法)
7.代理模式(Proxy)
意图:为其他对象提供一种代理以控制对这个对象的访问。
主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
何时使用:想在访问一个类时做一些控制。
如何解决:增加中间层。
关键代码:实现与被代理类组合。
应用实例: 1、Windows 里面的快捷方式。 2、猪八戒去找高翠兰结果是孙悟空变的,可以这样理解:把高翠兰的外貌抽象出来,高翠兰本人和孙悟空都实现了这个接口,猪八戒访问高翠兰的时候看不出来这个是孙悟空,所以说孙悟空是高翠兰代理类。 3、买火车票不一定在火车站买,也可以去代售点。 4、一张支票或银行存单是账户中资金的代理。支票在市场交易中用来代替现金,并提供对签发人账号上资金的控制。 5、spring aop。
优点: 1、职责清晰。 2、高扩展性。 3、智能化。
缺点: 1、由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。 2、实现代理模式需要额外的工作,有些代理模式的实现非常复杂。
使用场景:按职责来划分,通常有以下使用场景: 1、远程代理。 2、虚拟代理。 3、Copy-on-Write 代理。 4、保护(Protect or Access)代理。 5、Cache代理。 6、防火墙(Firewall)代理。 7、同步化(Synchronization)代理。 8、智能引用(Smart Reference)代理。
代码示例
- __author__ = "Burgess Zheng"
- #!/usr/bin/env python
- #-*- coding:utf-8 -*-
- '''
- Proxy
- '''
- # 代理模式
- # 应用特性:需要在通信双方中间需要一些特殊的中间操作时引用,多加一个中间控制层。
- # 结构特性:建立一个中间类,创建一个对象,接收一个对象,然后把两者联通起来
- class sender_base:
- def __init__(self):
- pass
- def send_something(self, something):
- pass
- class send_class(sender_base):
- def __init__(self, receiver):
- self.receiver = receiver
- def send_something(self, something):
- print("SEND " + something + ' TO ' + self.receiver.name)
- class agent_class(sender_base):
- def __init__(self, receiver):
- self.send_obj = send_class(receiver)
- def send_something(self, something):
- self.send_obj.send_something(something)
- class receive_class:
- def __init__(self, someone):
- self.name = someone
- if '__main__' == __name__:
- receiver = receive_class('Burgess')
- agent = agent_class(receiver)
- agent.send_something('agentinfo')
- print(receiver.__class__)
- print(agent.__class__)
执行结果:
sender_base(父类or基类)
send_class(sender_base的子类or派生类):作用为发送信息
agent_class(sender_base的子类or派生类):作用为代理(发送信息给接受者)
receive_class(新式类):作用为实例化一个接受者
8.装饰模式(Decorator)
意图:
动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator 模式相比生成子类更为灵活。
适用性:
在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
处理那些可以撤消的职责。
当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。
代码示例:
- __author__ = "Burgess Zheng"
- #!/usr/bin/env python
- #-*- coding:utf-8 -*-
- '''
- Decorator
- '''
- class foo(object):
- def f1(self):
- print("original f1")
- def f2(self):
- print("original f2")
- class foo_decorator(object):
- def __init__(self, decoratee):
- self._decoratee = decoratee
- def f1(self):
- print("decorated f1")
- self._decoratee.f1()
- def __getattr__(self, name):
- return getattr(self._decoratee, name)
- u = foo()
- v = foo_decorator(u)
- v.f1()
- v.f2()
执行结果:
从代码可以了解执行一个类对象可以通过另外一个类(装饰器)代替其执行(不改变被装饰的类的源码)
9.桥模式(Bridge、多维度)
- 生活中的一个例子:
- 就拿汽车在路上行驶的来说。即有小汽车又有公共汽车,它们都不但能在市区中的公路上行驶,也能在高速公路上行驶。这你会发现,对于交通工具(汽车)有不同的类型,然而它们所行驶的环境(路)也在变化,在软件系统中就要适应两个方面的变化?怎样实现才能应对这种变化呢?
- 概述:
- 在软件系统中,某些类型由于自身的逻辑,它具有两个或多个维度的变化,那么如何应对这种“多维度的变化”?如何利用面向对象的技术来使得该类型能够轻松的沿着多个方向进行变化,而又不引入额外的复杂度?这就要使用Bridge模式。
- 意图:
- 将抽象部分与实现部分分离,使它们都可以独立的变化。
- ——《设计模式》GOF
- 效果及实现要点:
- 1.Bridge模式使用“对象间的组合关系”解耦了抽象和实现之间固有的绑定关系,使得抽象和实现可以沿着各自的维度来变化。
- 2.所谓抽象和实现沿着各自维度的变化,即“子类化”它们,得到各个子类之后,便可以任意它们,从而获得不同路上的不同汽车。
- 3.Bridge模式有时候类似于多继承方案,但是多继承方案往往违背了类的单一职责原则(即一个类只有一个变化的原因),复用性比较差。Bridge模式是比多继承方案更好的解决方法。
- 4.Bridge模式的应用一般在“两个非常强的变化维度”,有时候即使有两个变化的维度,但是某个方向的变化维度并不剧烈——换言之两个变化不会导致纵横交错的结果,并不一定要使用Bridge模式。
- 适用性:
- 在以下的情况下应当使用桥梁模式:
- 1.如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的联系。
- 2.设计要求实现化角色的任何改变不应当影响客户端,或者说实现化角色的改变对客户端是完全透明的。
- 3.一个构件有多于一个的抽象化角色和实现化角色,系统需要它们之间进行动态耦合。
- 4.虽然在系统中使用继承是没有问题的,但是由于抽象化角色和具体化角色需要独立变化,设计要求需要独立管理这两者。
- 总结:
- Bridge模式是一个非常有用的模式,也非常复杂,它很好的符合了开放-封闭原则和优先使用对象,而不是继承这两个面向对象原则
示例代码
- __author__ = "Burgess Zheng"
- #!/usr/bin/env python
- #-*- coding:utf-8 -*-
- '''
- Bridge
- '''
- class AbstractRoad(object):
- '''路基类'''
- car = None
- class AbstractCar(object):
- '''车辆基类'''
- def run(self):
- raise NotImplementedError
- 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()
- road3 = Street()
- road3.car = Bus()
- road3.run()
执行结果:
AbstractRoad(父类or基类)
Street(AbstractRoad的子类or派生类):作用为执行了车辆对象的方法
SpeedWay(AbstractRoad的子类or派生类):作用为执行了车辆对象的方法
AbstractCar(父类or基类)
Car(AbstractCar的子类or派生类):作用为被调用执行
Bus(AbstractCar的子类or派生类):作为为被调用执行
应用设计模式:
桥接模式(Bridge)来做(多维度变化);
结合上面的例子,增加一个维度"人",不同的人开着不同的汽车在不同的路上行驶(三个维度);
结合上面增加一个类"人",并重新调用.
代码实现:
示例代码
- __author__ = "Burgess Zheng"
- #!/usr/bin/env python
- #-*- coding:utf-8 -*-
- '''
- 应用设计模式(Bridge多维度)
- '''
- class AbstractRoad(object):
- '''公路基类'''
- car = None
- class AbstractCar(object):
- '''车辆基类'''
- def run(self):
- pass
- class People(object):
- 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):
- '''公共汽车'''
- road = None
- def run(self):
- print("公共汽车在")
- #加上人
- class Man(People):
- def drive(self):
- print("男人开着")
- self.road.run()
- #加上人
- class Woman(People):
- def drive(self):
- print("女人开着")
- self.road.run()
- if __name__ == "__main__":
- #小汽车在高速上行驶
- road1 = SpeedWay()
- road1.car = Car()
- road1.run()
- #
- road2 = SpeedWay()
- road2.car = Bus()
- road2.run()
- #人开车
- road3 = Street()
- road3.car = Car()
- p1 = Man()
- p1.road = road3
- p1.drive()
- p2 = Woman()
- p2.road = road3
- p2.drive()
执行结果:
AbstractRoad(父类or基类)
Street(AbstractRoad的子类or派生类):作用为执行了车辆对象的方法
SpeedWay(AbstractRoad的子类or派生类):作用为执行了车辆对象的方法
AbstractCar(父类or基类)
Car(AbstractCar的子类or派生类):作用为被调用执行
Bus(AbstractCar的子类or派生类):作为为被调用执行
People(父类or基类)
Man(People的子类or派生类):作用为执行了路对象的方法
Woman(People的子类or派生类):作用为执行了路对象的方法
10.组合模式(Composite)
意图:
将对象组合成树形结构以表示“部分-整体”的层次结构。C o m p o s i t e 使得用户对单个对象和组合对象的使用具有一致性。
适用性:
你想表示对象的部分-整体层次结构。
你希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。
示例代码
- __author__ = "Burgess Zheng"
- # !/usr/bin/env python
- # -*- coding:utf-8 -*-
- """
- Composite
- """
- class Component:
- def __init__(self, strName):
- self.m_strName = strName
- def Add(self, com):
- pass
- def Display(self, nDepth):
- pass
- class Leaf(Component):
- def Add(self, com):
- print("leaf can't add")
- def Display(self, nDepth):
- strtemp = "-" * nDepth
- strtemp = strtemp + self.m_strName
- print(strtemp)
- class Composite(Component):
- def __init__(self, strName):
- self.m_strName = strName
- self.c = []
- def Add(self, com):
- self.c.append(com)
- def Display(self, nDepth):
- strtemp = "-" * nDepth
- strtemp = strtemp + self.m_strName
- print(strtemp)
- for com in self.c:
- com.Display(nDepth + 2)
- if __name__ == "__main__":
- p = Composite("Wong")
- p.Add(Leaf("Lee"))
- p.Add(Leaf("Zhao"))
- p1 = Composite("Wu")
- p1.Add(Leaf("San"))
- p.Add(p1)
- p.Display(1)
执行结果:
从执行结果可以看出,通过一个类对象把其他的类对象添加到自身的变量列表,然后循环执行该列表,执行所有对象的相同方法
11.外观模式(Facade)
意图:
为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
适用性:
当你要为一个复杂子系统提供一个简单接口时。子系统往往因为不断演化而变得越来越复杂。大多数模式使用时都会产生更多更小的类。这使得子系统更具可重用性,也更容易对子系统进行定制,但这也给那些不需要定制子系统的用户带来一些使用上的困难。Facade 可以提供一个简单的缺省视图,这一视图对大多数用户来说已经足够,而那些需要更多的可定制性的用户可以越过facade层。
客户程序与抽象类的实现部分之间存在着很大的依赖性。引入facade 将这个子系统与客户以及其他的子系统分离,可以提高子系统的独立性和可移植性。
当你需要构建一个层次结构的子系统时,使用facade模式定义子系统中每层的入口点。如果子系统之间是相互依赖的,你可以让它们仅通过facade进行通讯,从而简化了它们之间的依赖关系。
- __author__ = "Burgess Zheng"
- #!/usr/bin/env python
- #-*- coding:utf-8 -*-
- '''
- Facade
- '''
- #外观模式(Facade),为子系统中的一组接口提供一个一致的界面,定义一个高层接口,这个接口使得这一子系统更加容易使用。
- # 在以下情况下可以考虑使用外观模式:
- # (1)设计初期阶段,应该有意识的将不同层分离,层与层之间建立外观模式。
- # (2) 开发阶段,子系统越来越复杂,增加外观模式提供一个简单的调用接口。
- # (3) 维护一个大型遗留系统的时候,可能这个系统已经非常难以维护和扩展,但又包含非常重要的功能,为其开发一个外观类,以便新系统与其交互。
- # 优点编辑
- # (1)实现了子系统与客户端之间的松耦合关系。
- # (2)客户端屏蔽了子系统组件,减少了客户端所需处理的对象数目,并使得子系统使用起来更加容易。
- def printInfo(info):
- print(info)
- class Stock():
- name = '股票'
- def buy(self):
- printInfo('买 '+self.name)
- def sell(self):
- printInfo('卖 '+self.name)
- class ETF():
- name = '指数型基金'
- def buy(self):
- printInfo('买 '+self.name)
- def sell(self):
- printInfo('卖 '+self.name)
- class Future():
- name = '期货'
- def buy(self):
- printInfo('买 '+self.name)
- def sell(self):
- printInfo('卖 '+self.name)
- class NationDebt():
- name = '国债'
- def buy(self):
- printInfo('买 '+self.name)
- def sell(self):
- printInfo('卖 '+self.name)
- class Option():
- name = '权证'
- def buy(self):
- printInfo('买 '+self.name)
- def sell(self):
- printInfo('卖 '+self.name)
- #基金
- class Fund():
- def __init__(self):
- self.stock = Stock()
- self.etf = ETF()
- self.future = Future()
- self.debt = NationDebt()
- self.option = Option()
- def buyFund(self):
- self.stock.buy()
- self.etf.buy()
- self.debt.buy()
- self.future.buy()
- self.option.buy()
- def sellFund(self):
- self.stock.sell()
- self.etf.sell()
- self.future.sell()
- self.debt.sell()
- self.option.sell()
- def clientUI():
- myFund = Fund()
- myFund.buyFund()
- myFund.sellFund()
- return
- if __name__ == '__main__':
- clientUI()
执行结果:
从执行结果可以看出:一个类被实例化成对象,该对象的初始化进行了实例化所有其他的类对象,通过调用该类的方法执行所有其他类对象的相同方法
12.享元模式(Flyweight)
意图:
运用共享技术有效地支持大量细粒度的对象。
适用性:
一个应用程序使用了大量的对象。
完全由于使用大量的对象,造成很大的存储开销。
对象的大多数状态都可变为外部状态。
如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象。
应用程序不依赖于对象标识。由于Flyweight 对象可以被共享,对于概念上明显有别的对象,标识测试将返回真值。
代码示例:
- __author__ = "Burgess Zheng"
- #!/usr/bin/env python
- #-*- coding:utf-8 -*-
- '''
- Flyweight
- '''
- class FlyweightBase(object):
- _instances = dict() #皴法实例化的对象内存地址
- def __init__(self,*args,**kwargs):
- #继承的子类必须初始化
- raise NotImplementedError
- def __new__(cls, *args, **kwargs):
- print(cls._instances,type(cls)) #cls 就是你要实例化的子类如:obj = Spam(1,abc)
- return cls._instances.setdefault(
- (cls,args,tuple(kwargs.items())), #key (实例和参数)obj = Spam(y,x)
- super(FlyweightBase,cls).__new__(cls) # value #实例化新的对象的内存地址
- # 调用自身的_instances字典,如果没有往父类找_instances字典
- # setdefault:判断_instances字典是否有该key:obj = Spam(y,x)实例 ,
- # 如果有,返回该key的value(上次实例化对象(内存地址))
- # setdefault: 如果找不到key:obj = Spam(y,x)实例 ,就在_instances字典就创建该key,value为新实例化对象(内存地址)
- # 返回该新创建key的value(该次实例化的对象(内存地址)
Python工程之设计模式总结相关推荐
- Python 工程管理及 virtualenv 的迁移
virtualenv 是管理 python 工程的利器,它可以很好的帮你维护项目中的依赖,使用 virtualenv,还能保持 global 库的干净.不会被不同项目中的第三方库所污染. virtua ...
- Python工程的文档结构
Python工程的文档结构,可以参考https://stackoverflow.com/questions/193161/what-is-the-best-project-structure-for- ...
- Python工程目录组织
Python工程目录组织 from: https://zhuanlan.zhihu.com/p/36221226 Python工程目录组织 关于如何组织一个较好的Python工程目录结构,已经有一些得 ...
- 【开发环境】PyCharm 打开现有 Python 工程 ( 配置 Python 编译器版本 )
文章目录 一.PyCharm 打开现有 Python 工程 二.配置 Python 编译器版本 一.PyCharm 打开现有 Python 工程 在 PyCharm 欢迎界面 , 点击 " ...
- 【开发环境】Windows 安装 PyCharm 开发环境 ( 下载 PyCharm | 安装 PyCharm | 在 PyCharm 中创建 Python 工程 )
文章目录 一.下载 PyCharm 二.安装 PyCharm 三.在 PyCharm 中创建 Python 工程 一.下载 PyCharm 到 PyCharm 主页 https://www.jetbr ...
- python怎么新建工程_pycharm新建一个python工程步骤
小编最近由于工作原因要用到python,一门新的知识需要接触,对于我来说难度还是很大的. python工程目录结构 每次创建一个python工程 PyCharm会创建如下目录 创建时会把python运 ...
- Python工程能力进阶、数学基础、经典机器学习模型实战、深度学习理论基础和模型调优技巧……胜任机器学习工程师岗位需要学习什么?...
咱不敢谈人工智能时代咋样咋样之类的空话,就我自己来看,只要是个营收超过 5 亿的互联网公司,基本都需要具备机器学习的能力.因为大部分公司盈利模式基本都会围绕搜索.推荐和广告而去. 就比如极客时间,他的 ...
- 获取python工程下面的所需的所有开源依赖包
现在开源的项目网上很多,但是往往是不写依赖包的,导致我们不知道哪些包需要记录. 这个博文给出获取python工程下面所有开源依赖包的方法. 首先我找了有没有现成的包[1],该链接中显示的包是用来获取p ...
- python算法基础设计模式,python常见的设计模式
Python有设计模式么 Python设计模式主要分为三大类:创建型模式.结构型模式.行为型模式;三 大类中又被细分为23种设计模式,以下这几种是最常见的. 单例模式:是一种常用的软件设计模式,该模式 ...
最新文章
- 抱歉,我觉得有些人做副业并不靠谱
- 【Google Play】Android 应用用户协议 ( 生成用户协议 | HTML 用户协议模板 | Markdown 用户协议模板 )
- 《Too Much Heaven》
- python excel centos_centos中使用python遇到的几个问题
- pyplot交互地画多个plot
- python爬虫反爬 css 知乎 专栏_反反爬虫系列(四)
- wx.getBLEDeviceCharacteristics 微信小程序蓝牙 微信小程序热敏打印机
- 只需两步手把手教你玩转图像识别
- linux操作指令训练,实验二linux 常用命令练习
- 张朝阳:Q4盈利远超预期 2021年期待产品爆发
- 基于JAVA+SpringMVC+Mybatis+MYSQL的周报管理系统
- java fork join demo_Fork/Join框架 demo
- android文件管理器,10款优秀的Android文件管理器
- 富士施乐P375 d打印机设置网络地址
- win10打开计算机黑屏怎么办,win10系统重启黑屏怎么办
- Beijing's Antitrust Blunder
- phpcms 设置page页码 首页、末页、总数据
- 解决pycharm导入自己写的模块飘红问题
- Zig-Zag(Z型)填数
- 修改注册表使任务栏全透明
热门文章
- xp无任何网络提供程序接受指定的网络路径解决方法--win7w.com
- plSql使用技巧及优化
- IOS多线程开发详解
- 《离散数学及其应用》读书笔记【三】计数
- 详细TinyMCE汉日英语言包,图片上传等vue2
- 基于MATLAB樽海鞘算法SSA(麻雀算法(十一)也简称SSA)的Eggholeer函数优化,测试函数的100种优化方法之十二
- 2022 Apache IoTDB 物联网生态大会 | 早鸟超值获票福利第一弹!(限时 3 天)
- 2017中国(上海)国际物联网大会在沪举办
- windows下修改文件创建时间
- 工作方法论: 请别跟我说“帮我解决一个问题”