企业网站运营外包费用世界十大搜索引擎排名
web/
2025/10/1 16:36:58/
文章来源:
企业网站运营外包费用,世界十大搜索引擎排名,qq小程序搭建,微信小程序开发方案怎么做目录 设计模式what?why?设计模式#xff1a;设计模式也衍生出了很多的新的种类#xff0c;不局限于这23种创建类设计模式#xff08;5种#xff09;结构类设计模式#xff08;7种#xff09;行为类设计模式#xff08;11种#xff09; 六大设计原则开闭原则里氏替换原… 目录 设计模式what?why?设计模式设计模式也衍生出了很多的新的种类不局限于这23种创建类设计模式5种结构类设计模式7种行为类设计模式11种 六大设计原则开闭原则里氏替换原则依赖倒置原则接口隔离原则迪米特法则最少知识原则单一职责原则 设计原则与设计模式创建类设计模式与设计原则结构类设计模式与设计原则行为类设计模式与设计原则 创建类设计模式5种单例模式使用场景优缺点代码实现python代码go代码 工厂模式简单工厂模式抽象工厂模式使用场景优缺点代码实现python代码go代码 建造者模式使用场景优缺点代码实现python代码go代码 原型模式使用场景优缺点代码实现python代码go代码 结构类设计模式7种代理模式使用场景优缺点代码实现python代码go代码 装饰器模式使用场景优缺点代码实现python代码go代码 适配器模式使用场景优缺点代码实现python代码go代码 门面模式/外观模式/Facade Pattern使用场景优缺点代码实现python代码go代码 组合模式使用场景优缺点代码实现python代码go代码 享元模式使用场景优缺点代码实现python代码go代码 桥接模式使用场景优缺点代码实现python代码go代码 行为类设计模式11种策略模式使用场景优缺点代码实现python代码go代码 责任链模式使用场景优缺点代码实现python代码go代码 命令模式使用场景优缺点代码实现python代码go代码 中介者模式使用场景优缺点代码实现python代码go代码 模板模式使用场景优缺点代码实现python代码go代码 迭代器模式使用场景优缺点代码实现python代码go代码 访问者模式使用场景优缺点代码实现python代码go代码 观察者模式使用场景优缺点代码实现python代码go代码 解释器模式使用场景优缺点代码实现python代码go代码 备忘录模式使用场景优缺点代码实现python代码go代码 状态模式使用场景优缺点代码实现python代码go代码 设计模式
what?
设计模式是面对各种问题进行提炼和抽象而形成的解决方案。
这些设计方案是前人不断试验考虑了封装性、复用性、效率、可修改、可移植等各种因素的高度总结。
它不限于一种特定的语言它是一种解决问题的思想和方法。
why?
由于高级语言的出现让机器读懂你的意图已经不是最主要的“矛盾”而让人读懂你的意图才是最重要。按照设计模式编写的代码其可读性也会大大提升利于团队项目的继承和扩展。
设计模式设计模式也衍生出了很多的新的种类不局限于这23种
设计模式可以分为三个大类创建类设计模式、结构类设计模式、行为类设计模式
下面用一个图片来整体描述一下设计模式之间的关系 创建类设计模式5种
单例模式、工厂模式简单工厂模式、抽象工厂模式、建造者模式、原型模式
结构类设计模式7种
代理模式、装饰器模式、适配器模式、门面模式、组合模式、享元模式、桥梁模式
行为类设计模式11种
策略模式、责任链模式、命令模式、中介者模式、模板模式、迭代器模式、访问者模式、观察者模式、解释器模式、备忘录模式、状态模式
六大设计原则
设计模式与设计原则基本符合规则与原则的关系设计模式是一个个具体问题的解决方案设计原则则反映了这些设计模式的指导思想同时设计原则可衍生出的设计模式也不仅限于上述介绍到了23种设计模式任何一种针对特定业务场景中的解决方法虽然找不到对应的设计模式与之匹配但若符合设计原则也可以认为是一种全新的设计模式。从这个意义上来说设计模式是程序设计方法的形而设计原则是程序设计方法的神。
开闭原则
开闭原则英文原名为Open Closed Principle简称OCP原则。其含义为一个软件实体如类、模块、函数等应该对扩展开放对修改关闭。
开闭原则是非常基础的一个原则也有人把开闭原则称为“原则的原则”。模块分原子模块低层模块高层模块业务层可以认为是最高层次的模块。
对扩展开放意味着模块的行为是可以扩展的当高层模块需求改变时可以对低层模块进行扩展使其具有满足高层模块的新功能对修改关闭即对低层模块行为进行扩展时不必改动模块的源代码。最理想的情况是业务变动时仅修改业务代码不修改依赖的模块类、函数等代码通过扩展依赖的模块单元来实现业务变化。
举例说明假设一个原始基类水果类苹果类是它的派生类苹果中包含水果的各种属性如形状、颜色等另有两个类农民类和花园类最高层次业务层次为农民在花园种苹果。如果此时农民决定不种苹果了改种梨符合OCP原则的设计应该为基于水果类构建一个新的类即梨类对扩展开放而并不应该去修改苹果类使它成为一个梨类对修改关闭。修改应仅在最高层即业务层中进行。
里氏替换原则
里氏替换原则英文原名为Liskov Substitution Principle简称LSP原则。它是面向对象设计的最为基本原则之一。 里氏替换原则的含义为任何基类可以出现的地方子类一定可以出现。 LSP是继承复用的基石只有当子类可以替换掉基类软件单位的功能不受到影响时基类才能真正被复用子类也能够在基类的基础上增加新的行为。
举例说明对于一个鸟类可以衍生出麻雀、喜鹊、布谷等子类这些子类都可继承鸟类的鸣叫、飞行、吃食等接口。而对于一个鸡类虽然它在生物学上属于鸟类但它不会飞那么符合LSP设计原则的情况下鸡就不应该是鸟的一个子类在鸟类调用飞行接口的地方鸡类并不能出现。如果鸡类要使用鸟类的接口应该使用关联关系而不是继承关系。
依赖倒置原则
依赖倒置原则英文原名为Dependence Inversion Principle简称DIP原则。它的含义为高层模块不应该依赖于低层模块两者都应该依赖其抽象。抽象不应该依赖于细节细节应该依赖于抽象。
将每个不可细分的逻辑叫作原子逻辑原子逻辑组装形成低层模块低层模块组装形成高层模块。依赖倒置原则的含义为高层模块和低层模块都应该由各自的抽象模块派生而来同时接口设计应该依赖于抽象而非具体模块。
举例说明司机与汽车是依赖的关系司机可以有实习司机类、老司机类等派生汽车可以有轿车、SUV、卡车等派生类。如果司机中设计一个接口drive汽车是其参数符合DIP设计原则的参数应该是在基类司机类中将基类汽车类作为参数而司机的派生类中drive的参数同样应该为基类汽车类而不应该是汽车类的任一个派生类。如果规定实习司机只能开轿车等业务逻辑应该在其接口中进行判断而不应该将参数替换成子类轿车。
接口隔离原则
接口隔离原则英文原名为Interface Segregation Principle简称ISP原则。其含义为类间的依赖关系不应该建立一个大的接口而应该建立其最小的接口即客户端不应该依赖那些它不需要的接口。这里的接口的概念是非常重要的。从逻辑上来讲这里的接口可以指一些属性和方法的集合从业务上来讲接口就可以指特定业务下的接口如函数URL调用等。接口应该尽量小同时仅留给客户端必要的接口弃用没有必要的接口。
举例说明如果要根据具体的数据生成饼图、直方图、表格这个类该如何设计如果将生成饼图、直方图、表格等“接口”这里的接口就是“操作”的集合的概念写在一个类中是不符合接口隔离原则的。符合ISP原则的设计应该是设计三个类每个类分别实现饼图、直方图、表格的绘制。
接口隔离原则和单一职责原则一样涉及到粒度的问题解决粒度大小同样依赖于具体的业务场景需要读者根据实践去权衡。
迪米特法则最少知识原则
迪米特法则Law of Demeter也叫最少知识原则英文Least Knowledge Principle简称LKP原则。其含义为一个对象应该对其它对象有最少的了解。
举例说明一个公司有多个部门每个部门有多个员工如果公司CEO要下发通知给每个员工是调用接口直接通知所有员工么其实不然CEO只需和它的“朋友”类部门Leader交流就好部门Leader再下发通知信息即可。而CEO类不需要与员工进行“交流”。
迪米特法则要求对象应该仅对自己的朋友类交流而不应该对非朋友类交流。那什么才是朋友类呢一般来说朋友类具有以下特征
1当前对象本身self 2以参量形式传入到当前对象方法中的对象 3当前对象的实例变量直接引用的对象 4当前对象的实例变量如果是一个聚集那么聚集中的元素也都是朋友 5当前对象所创建的对象。
单一职责原则
单一职责原则英文原名为Single Responsibility Principle简称SRP原则。其含义为应该有且仅有一个原因引起类的变更。
举例说明一个视频播放系统一个客户端类有两个功能接口即视频播放接口和音频播放接口。虽然这样的设计很常见但却不满足单一职责原则的。原因是如果对视频播放有变更需求或者对音频播放有修改需求都会变更视频客户端的类结构。符合单一原则的设计是将视频播放单元和音频播放单元各建一个类播放客户端继承两个类构成客户端。
单一职责原则的最大难点在于职责的划分试想以上划分是否是符合单一职责了既是也不是。试想如果将视频传输和音频传输的协议信息和数据信息区分开为符合这种粒度的单一职责原则就必须要有协议传输类和数据传输类的划分。如果接着细分可能一个简单的小模块都要设计非常多的类。因此单一职责原则粒度的选择应该根据业务流程和人员分工来进行考虑。一些基本的划分似乎已经成了行业规范性的内容比如业务逻辑与用户信息管理的划分等。
设计原则与设计模式
创建类设计模式与设计原则
工厂模式工厂方法模式是一种解耦结构工厂类只需要知道抽象产品类符合最少知识原则迪米特法则同时符合依赖倒置原则和里氏替换原则
抽象工厂模式抽象工厂模式具有工厂模式的优点但同时如果产品族要扩展工厂类也要修改违反了开闭原则
模板模式优秀的扩展能力符合开闭原则。
结构类设计模式与设计原则
代理模式代理模式在业务逻辑中将对主体对象的操作进行封装合适的应用会符合开闭原则和单一职责原则事实上几乎带有解耦作用的结构类设计模式都多少符合些开闭原则
门面模式门面模式不符合开闭原则有时不符合单一职责原则如若不注意也会触碰接口隔离原则
组合模式符合开闭原则但由于一般在拼接树时使用实现类故不符合依赖倒置原则
桥梁模式桥梁模式堪称依赖倒置原则的典范同时也符合开闭原则。
行为类设计模式与设计原则
策略模式符合开闭原则但高层模块调用时不符合迪米特法则。行为类设计模式多少会符合些单一职责原则典型的如观察者模式、中介者模式、访问者模式等
责任链模式符合单一职责原则和迪米特法则
命令模式符合开闭原则。
在不同的业务逻辑中不同的设计模式也会显示出不同的设计原则特点从这个意义上来说设计模式是设计原则的体现但体现不是固定的是根据业务而有所不同的。
创建类设计模式5种
单例模式
单例模式涉及到一个单一的类该类负责创建自己的对象同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式可以直接访问不需要实例化该类的对象。
单例模式是一种创建型设计模式它确保一个类只有一个实例并提供了一个全局访问点来访问该实例。
注意
单例类只能有一个实例。单例类必须自己创建自己的唯一实例。单例类必须给所有其他对象提供这一实例。
使用场景
意图保证一个类仅有一个实例并提供一个访问它的全局访问点。
主要解决一个全局使用的类频繁地创建与销毁。
何时使用当您想控制实例数目节省系统资源的时候。
如何解决判断系统是否已经有这个单例如果有则返回如果没有则创建。
关键代码构造函数是私有的。
应用实例
一个班级只有一个班主任。Windows 是多进程多线程的在操作一个文件的时候就不可避免地出现多个进程或线程同时操作一个文件的现象所以所有文件的处理必须通过唯一的实例来进行。一些设备管理器常常设计为单例模式比如一个电脑有两台打印机在输出的时候就要处理不能两台打印机打印同一个文件。要求生产唯一序列号。WEB 中的计数器不用每次刷新都在数据库里加一次用单例先缓存起来。创建的一个对象需要消耗的资源过多比如 I/O 与数据库的连接等。
优缺点
优点
在内存里只有一个实例减少了内存的开销尤其是频繁的创建和销毁实例比如管理学院首页页面缓存。避免对资源的多重占用比如写文件操作。
缺点
没有接口不能继承与单一职责原则冲突一个类应该只关心内部逻辑而不关心外面怎么样来实例化。单例模式是并发协作软件模块中需要最先完成的因而其不利于测试单例模式在某种情况下会导致“资源瓶颈”。
代码实现 以实现一个日志handle为例
python代码
# logger.py
from threading import Lock
from typing import Selfmutex Lock()class Logger:classmethoddef get_logger(cls) - Self:if not hasattr(cls, __single__):# 加锁实例化一次with mutex:if not hasattr(cls, __single__):cls.__single__ Logger()# 实例化后返回return cls.__single__# main.py
from logger import Logger
from concurrent.futures import ThreadPoolExecutordef test():logger Logger.get_logger()print(hex(id(logger)))if __name__ __main__:with ThreadPoolExecutor() as pool:for _ in range(10):pool.submit(test)执行结果
0x1364a000b10
0x1364a000b10
0x1364a000b10
0x1364a000b10
0x1364a000b100x1364a000b100x1364a000b10
0x1364a000b100x1364a000b10
0x1364a000b10go代码
golang提供了sync.Once使得开发者可以更容易的写出单例代码
// logger.go
package mainimport (sync
)var (once new(sync.Once)
)var L *Loggertype Logger struct {
}func getLogger() *Logger {once.Do(func() {L new(Logger)})return L
}// main.go
package mainimport (fmtsync
)func Task() {l : getLogger()fmt.Printf(%p \n, l)
}func main() {wg : new(sync.WaitGroup)wg.Add(10)for i : 0; i 10; i {go func() {defer wg.Done()Task()}()}wg.Wait()
}执行结果
0x5da5c0
0x5da5c0
0x5da5c0
0x5da5c0
0x5da5c0
0x5da5c0
0x5da5c0
0x5da5c0
0x5da5c0
0x5da5c0工厂模式简单工厂模式抽象工厂模式
工厂模式Factory Pattern是 Java 中最常用的设计模式之一这种类型的设计模式属于创建型模式它提供了一种创建对象的最佳方式。
工厂模式提供了一种创建对象的方式而无需指定要创建的具体类。
工厂模式属于创建型模式它在创建对象时提供了一种封装机制将实际创建对象的代码与使用代码分离。
使用场景
意图定义一个创建对象的接口让其子类自己决定实例化哪一个工厂类工厂模式使其创建过程延迟到子类进行。
主要解决主要解决接口选择的问题。
何时使用我们明确地计划不同条件下创建不同实例时。
如何解决让其子类实现工厂接口返回的也是一个抽象的产品。
关键代码创建过程在其子类执行。
应用实例
您需要一辆汽车可以直接从工厂里面提货而不用去管这辆汽车是怎么做出来的以及这个汽车里面的具体实现。在 Hibernate 中如果需要更换数据库工厂模式同样发挥了作用。只需简单地更改方言Dialect和数据库驱动Driver就能够实现对不同数据库的切换。日志记录器记录可能记录到本地硬盘、系统事件、远程服务器等用户可以选择记录日志到什么地方。数据库访问当用户不知道最后系统采用哪一类数据库以及数据库可能有变化时。设计一个连接服务器的框架需要三个协议“POP3”、“IMAP”、“HTTP”可以把这三个作为产品类共同实现一个接口。
注意事项作为一种创建类模式在任何需要生成复杂对象的地方都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式而简单对象特别是只需要通过 new 就可以完成创建的对象无需使用工厂模式。如果使用工厂模式就需要引入一个工厂类会增加系统的复杂度。
工厂模式包含以下几个核心角色
抽象产品Abstract Product定义了产品的共同接口或抽象类。它可以是具体产品类的父类或接口规定了产品对象的共同方法。具体产品Concrete Product实现了抽象产品接口定义了具体产品的特定行为和属性。抽象工厂Abstract Factory声明了创建产品的抽象方法可以是接口或抽象类。它可以有多个方法用于创建不同类型的产品。具体工厂Concrete Factory实现了抽象工厂接口负责实际创建具体产品的对象。
优缺点
优点
一个调用者想创建一个对象只要知道其名称就可以了。扩展性高如果想增加一个产品只要扩展一个工厂类就可以。屏蔽产品的具体实现调用者只关心产品的接口。工厂模式巨有非常好的封装性代码结构清晰在抽象工厂模式中其结构还可以随着需要进行更深或者更浅的抽象层级调整非常灵活屏蔽产品类使产品的被使用业务场景和产品的功能细节可以分而开发进行是比较典型的解耦框架。
缺点
每次增加一个产品时都需要增加一个具体类和对象实现工厂使得系统中类的个数成倍增加在一定程度上增加了系统的复杂度同时也增加了系统具体类的依赖这并不是什么好事。工厂模式相对于直接生成实例过程要复杂一些所以在小项目中可以不使用工厂模式抽象工厂模式中产品类的扩展比较麻烦。毕竟每一个工厂对应每一类产品产品扩展就意味着相应的抽象工厂也要扩展
代码实现
创建一个 Shape 接口和实现 Shape 接口的实体类。下一步是定义工厂类 ShapeFactory。
FactoryPatternDemo 类使用 ShapeFactory 来获取 Shape 对象。它将向 ShapeFactory 传递信息CIRCLE / RECTANGLE / SQUARE以便获取它所需对象的类型。
python代码
class ShapeInterface:def draw(self):raise NotImplementedError(this method must be implemented)class Circle(ShapeInterface):def draw(self):# do somethingprint(fthe method of the {self.__class__.__name__} is called)class Square(ShapeInterface):def draw(self):# do somethingprint(fthe method of the {self.__class__.__name__} is called)class Rectangle(ShapeInterface):def draw(self):# do somethingprint(fthe method of the {self.__class__.__name__} is called)class ShapeFactory:classmethoddef get_shape(cls, classname: str):match classname:case circle:return Circle()case square:return Square()case rectangle:return Rectangle()case _:return Noneif __name__ __main__:s ShapeFactory.get_shape(circle)s.draw()s ShapeFactory.get_shape(square)s.draw()s ShapeFactory.get_shape(rectangle)s.draw()执行结果
the method of the Circle is called
the method of the Square is called
the method of the Rectangle is calledgo代码
go语言原生提供了interface的思想让工厂模式实现更加简单
package mainimport fmttype ShapeInterface interface {Draw()
}type Rectangle struct {
}func (r *Rectangle) Draw() {fmt.Println(The method of the rectangle Draw is called)
}type Circle struct {
}func (c *Circle) Draw() {fmt.Println(The method of the circle Draw is called)
}type Square struct {
}func (s *Square) Draw() {fmt.Println(The method of the Square Draw is called)
}func GetShape(classname string) ShapeInterface {switch classname {case circle:return new(Circle)case square:return new(Square)case rectangle:return new(Rectangle)default:return nil}
}func main() {s : GetShape(circle)s.Draw()s GetShape(square)s.Draw()s GetShape(rectangle)s.Draw()
}在场景中写成如下形式
spicy_chicken_burgersimpleFoodFactory.createFood(spicyChickenBurger)省去了将工厂实例化的过程。这种模式就叫做简单工厂模式。还是在上述例子中createFood方法中必须传入foodClass才可以指定生成的food实例种类如果将每一个细致的产品都建立对应的工厂如cheeseBurger建立对应一个cheeseBurgerFactory这样生成食物时foodClass也不必指定。此时burgerFactory就是具体食物工厂的一层抽象。这种模式就是抽象工厂模式。 建造者模式
建造者模式Builder Pattern使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式它提供了一种创建对象的最佳方式。
一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。
使用场景
意图将一个复杂的构建与其表示相分离使得同样的构建过程可以创建不同的表示。
主要解决主要解决在软件系统中有时候面临着一个复杂对象的创建工作其通常由各个部分的子对象用一定的算法构成由于需求的变化这个复杂对象的各个部分经常面临着剧烈的变化但是将它们组合在一起的算法却相对稳定。
何时使用一些基本部件不会变而其组合经常变化的时候。
如何解决将变与不变分离开。
关键代码建造者创建和提供实例导演管理建造出来的实例的依赖关系。
应用实例
去肯德基汉堡、可乐、薯条、炸鸡翅等是不变的而其组合是经常变化的生成出所谓的套餐。go 中的 StringBuilder。需要生成的对象具有复杂的内部结构。需要生成的对象内部属性本身相互依赖。
建造者模式在创建复杂对象时非常有用特别是当对象的构建过程涉及多个步骤或参数时。它可以提供更好的灵活性和可维护性同时使得代码更加清晰可读。
注意事项与工厂模式的区别是建造者模式更加关注与零件装配的顺序。
优缺点
优点
分离构建过程和表示使得构建过程更加灵活可以构建不同的表示。封装性好用户可以不知道对象的内部构造和细节就可以直接建造对象系统扩展容易可以更好地控制构建过程隐藏具体构建细节。代码复用性高可以在不同的构建过程中重复使用相同的建造者。
缺点
如果产品的属性较少建造者模式可能会导致代码冗余。建造者模式增加了系统的类和对象数量。
代码实现
假设一个快餐店的商业案例其中一个典型的套餐可以是一个汉堡Burger和一杯冷饮Cold drink。汉堡Burger可以是素食汉堡Veg Burger或鸡肉汉堡Chicken Burger它们是包在纸盒中。冷饮Cold drink可以是可口可乐coke或百事可乐pepsi它们是装在瓶子中。
我们将创建一个表示食物条目比如汉堡和冷饮的 Item 接口和实现 Item 接口的实体类以及一个表示食物包装的 Packing 接口和实现 Packing 接口的实体类汉堡是包在纸盒中冷饮是装在瓶子中。
然后我们创建一个 Meal 类带有 Item 的 ArrayList 和一个通过结合 Item 来创建不同类型的 Meal 对象的 MealBuilder。BuilderPatternDemo 类使用 MealBuilder 来创建一个 Meal。 python代码
主餐
class Burger():nameprice0.0def getPrice(self):return self.pricedef setPrice(self,price):self.pricepricedef getName(self):return self.name
class cheeseBurger(Burger):def __init__(self):self.namecheese burgerself.price10.0
class spicyChickenBurger(Burger):def __init__(self):self.namespicy chicken burgerself.price15.0
小食
class Snack():name price 0.0type SNACKdef getPrice(self):return self.pricedef setPrice(self, price):self.price pricedef getName(self):return self.nameclass chips(Snack):def __init__(self):self.name chipsself.price 6.0class chickenWings(Snack):def __init__(self):self.name chicken wingsself.price 12.0饮料
class Beverage():name price 0.0type BEVERAGEdef getPrice(self):return self.pricedef setPrice(self, price):self.price pricedef getName(self):return self.nameclass coke(Beverage):def __init__(self):self.name cokeself.price 4.0class milk(Beverage):def __init__(self):self.name milkself.price 5.0建造一个订单因而需要一个订单类。假设一个订单包括一份主食一份小食一种饮料。
class order():burgersnackbeveragedef __init__(self,orderBuilder):self.burgerorderBuilder.bBurgerself.snackorderBuilder.bSnackself.beverageorderBuilder.bBeveragedef show(self):print Burger:%s%self.burger.getName()print Snack:%s%self.snack.getName()print Beverage:%s%self.beverage.getName()class orderBuilder():bBurgerbSnackbBeveragedef addBurger(self,xBurger):self.bBurgerxBurgerdef addSnack(self,xSnack):self.bSnackxSnackdef addBeverage(self,xBeverage):self.bBeveragexBeveragedef build(self):return order(self)if __name____main__:order_builderorderBuilder()order_builder.addBurger(spicyChickenBurger())order_builder.addSnack(chips())order_builder.addBeverage(milk())order_1order_builder.build()order_1.show()打印结果如下
Burger:spicy chicken burger
Snack:chips
Beverage:milkgo代码
go中的string build就是很好的建造者的例子
package mainimport stringsfunc main() {builder : new(strings.Builder)builder.WriteString()s : builder.String()
}builder通过Write类型方法不断往buf中“建造”内容最后通过String()方法将其show出来。
只不过go中的“小吃主食饮料”是bytestringrune。
原型模式
原型模式Prototype Pattern是用于创建重复的对象同时又能保证性能。这种类型的设计模式属于创建型模式它提供了一种创建对象的最佳方式之一。
这种模式是实现了一个原型接口该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时则采用这种模式。例如一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象在下一个请求时返回它的克隆在需要的时候更新数据库以此来减少数据库调用。
使用场景
意图用原型实例指定创建对象的种类并且通过拷贝这些原型创建新的对象。
主要解决在运行期建立和删除原型。
何时使用 1、当一个系统应该独立于它的产品创建构成和表示时。 2、当要实例化的类是在运行时刻指定时例如通过动态装载。 3、为了避免创建一个与产品类层次平行的工厂类层次时。 4、当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。
如何解决利用已有的一个原型对象快速地生成和原型对象一样的实例。
关键代码 1、实现克隆操作在 JAVA 实现 Cloneable 接口重写 clone()在 .NET 中可以使用 Object 类的 MemberwiseClone() 方法来实现对象的浅拷贝或通过序列化的方式来实现深拷贝。 2、原型模式同样用于隔离类对象的使用者和具体类型易变类之间的耦合关系它同样要求这些易变类拥有稳定的接口。
应用实例 1、细胞分裂。 2、JAVA 中的 Object clone() 方法。
使用场景 1、资源优化场景。 2、类初始化需要消化非常多的资源这个资源包括数据、硬件资源等。 3、性能和安全要求的场景。 4、通过 new 产生一个对象需要非常繁琐的数据准备或访问权限则可以使用原型模式。 5、一个对象多个修改者的场景。 6、一个对象需要提供给其他对象访问而且各个调用者可能都需要修改其值时可以考虑使用原型模式拷贝多个对象供调用者使用。 7、在实际项目中原型模式很少单独出现一般是和工厂方法模式一起出现通过 clone 的方法创建一个对象然后由工厂方法提供给调用者。原型模式已经与 Java 融为浑然一体大家可以随手拿来使用。
注意事项与通过对一个类进行实例化来构造新对象不同的是原型模式是通过拷贝一个现有对象生成新对象的。浅拷贝实现 Cloneable重写深拷贝是通过实现 Serializable 读取二进制流。
优缺点
优点 1、性能提高。 2、逃避构造函数的约束。
缺点 1、配备克隆方法需要对类的功能进行通盘考虑这对于全新的类不是很难但对于已有的类不一定很容易特别当一个类引用不支持串行化的间接对象或者引用含有循环结构的时候。 2、必须实现 Cloneable 接口。
代码实现
创建一个抽象类 Shape 和扩展了 Shape 类的实体类。下一步是定义类 ShapeCache该类把 shape 对象存储在一个 Hashtable 中并在请求的时候返回它们的克隆。
PrototypePatternDemo 类使用 ShapeCache 类来获取 Shape 对象。
python代码
from copy import copyclass CloneInterface:def clone(self):return copy(self)class Circle(CloneInterface):passclass Square(CloneInterface):passclass ShapeCache:__cache__: dict[str, CloneInterface] dict()classmethoddef load_cache(cls):cls.__cache__[1] Circle()cls.__cache__[2] Square()classmethoddef get_shape(cls, _id: str):return cls.__cache__[_id].clone()go代码
package mainimport fmttype Cloneable interface {Clone() Cloneable
}type Circle struct {Id int
}func (c *Circle) Clone() Cloneable {return Circle{Id: c.Id}
}type Square struct {Id int
}func (s *Square) Clone() Cloneable {return Square{Id: s.Id}
}type ShapeCache struct {cache map[string]Cloneable
}func LoadCache() *ShapeCache {c : ShapeCache{cache: map[string]Cloneable{1: Circle{Id: 1},2: Square{Id: 2},}}return c
}func (s *ShapeCache) GetShape(id string) Cloneable {return s.cache[id].Clone()
}func main() {s : LoadCache()shape : s.GetShape(1)fmt.Println(shape)
}结构类设计模式7种
代理模式
在代理模式Proxy Pattern中一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。
在代理模式中我们创建具有现有对象的对象以便向外界提供功能接口。
使用场景
意图为其他对象提供一种代理以控制对这个对象的访问。
主要解决在直接访问对象时带来的问题比如说要访问的对象在远程的机器上。在面向对象系统中有些对象由于某些原因比如对象创建开销很大或者某些操作需要安全控制或者需要进程外的访问直接访问会给使用者或者系统结构带来很多麻烦我们可以在访问此对象时加上一个对此对象的访问层。
何时使用想在访问一个类时做一些控制。
如何解决增加中间层。
关键代码实现与被代理类组合。
使用场景按职责来划分通常有以下使用场景
远程代理。虚拟代理。Copy-on-Write 代理。保护Protect or Access代理。Cache代理。防火墙Firewall代理。业务系统的非功能性需求开发如监控、统计、鉴权、限流、事务、幂等、日志等这些和业务没有关系所以可以放到Proxy中RealSubject只关注功能性需求。
注意事项
和适配器模式的区别适配器模式主要改变所考虑对象的接口而代理模式不能改变所代理类的接口。和装饰器模式的区别装饰器模式为了增强功能而代理模式是为了加以控制。
主要涉及到以下几个核心角色
抽象主题Subject:定义了真实主题和代理主题的共同接口这样在任何使用真实主题的地方都可以使用代理主题。真实主题Real Subject:实现了抽象主题接口是代理对象所代表的真实对象。客户端直接访问真实主题但在某些情况下可以通过代理主题来间接访问。代理Proxy:实现了抽象主题接口并持有对真实主题的引用。代理主题通常在真实主题的基础上提供一些额外的功能例如延迟加载、权限控制、日志记录等。客户端Client:使用抽象主题接口来操作真实主题或代理主题不需要知道具体是哪一个实现类。
优缺点
优点
职责清晰非常符合单一职责原则主题对象实现真实业务逻辑而非本职责的事务交由代理完成扩展性强面对主题对象可能会有的改变代理模式在不改变对外接口的情况下可以实现最大程度的扩展保证主题对象的处理逻辑代理可以通过检查参数的方式保证主题对象的处理逻辑输入在理想范围内。
缺点
由于在客户端和真实主题之间增加了代理对象因此有些类型的代理模式可能会造成请求的处理速度变慢。实现代理模式需要额外的工作有些代理模式的实现非常复杂。
代码实现 对接大量的第三方支付公司PayU、PayTM、WX这些公司发起支付的流程是一样的核心是获取token但是还要做很多琐碎、通用的工作如校验签名、初始化订单数据、参数检查、记录日志等。这些琐碎功能如果让每一个支付类自己处理不但是重复开发而且后期修改时不易维护这时候就很适合用代理模式。 python代码
class PayInterface:def pay(self, order_id: str) - str:raise NotImplementedError()class WxPay(PayInterface):def pay(self, order_id: str) - str:return 从微信获取支付tokenclass AliPay(PayInterface):def pay(self, order_id: str) - str:return 从阿里获取支付tokenclass PayProxy(PayInterface):def __init__(self, real_pay: PayInterface):self.real_pay real_paydef pay(self, order_id: str) - str:print(处理 order_id)print(1校验签名)print(2格式化订单数据)print(3参数检查)print(4记录请求日志)token self.real_pay.pay(order_id)return http://组装 token 然后跳转到第三方支付if __name__ __main__:proxy PayProxy(AliPay())url proxy.pay(阿里订单)print(url)执行结果
处理阿里订单
1校验签名
2格式化订单数据
3参数检查
4记录请求日志
http://组装从阿里获取支付token然后跳转到第三方支付go代码
package mainimport (fmt
)type PaymentService interface {pay(string) string
}type WXPay struct {
}func (w *WXPay) pay(order string) string {return 从微信获取支付token
}type AliPay struct {
}func (a *AliPay) pay(order string) string {return 从阿里获取支付token
}type PaymentProxy struct {realPay PaymentService
}func (p *PaymentProxy) pay(order string) string {fmt.Println(处理 order)fmt.Println(1校验签名)fmt.Println(2格式化订单数据)fmt.Println(3参数检查)fmt.Println(4记录请求日志)token : p.realPay.pay(order)return http://组装 token 然后跳转到第三方支付
}
func main() {proxy : PaymentProxy{realPay: AliPay{},}url : proxy.pay(阿里订单)fmt.Println(url)
}再使用上工厂模式就全自动化了增加新的支付类只需要实现接口即可。
装饰器模式
装饰器模式Decorator Pattern允许向一个现有的对象添加新的功能同时又不改变其结构。这种类型的设计模式属于结构型模式它是作为现有的类的一个包装。
装饰器模式通过将对象包装在装饰器类中以便动态地修改其行为。
这种模式创建了一个装饰类用来包装原有的类并在保持类方法签名完整性的前提下提供了额外的功能。
使用场景
意图动态地给一个对象添加一些额外的职责。就增加功能来说装饰器模式相比生成子类更为灵活。
主要解决一般的我们为了扩展一个类经常使用继承方式实现由于继承为类引入静态特征并且随着扩展功能的增多子类会很膨胀。
何时使用在不想增加很多子类的情况下扩展类。
如何解决将具体功能职责划分同时继承装饰者模式。
使用场景
扩展一个类的功能。动态增加功能动态撤销。
注意事项可代替继承。
装饰器模式包含以下几个核心角色
抽象组件Component定义了原始对象和装饰器对象的公共接口或抽象类可以是具体组件类的父类或接口。具体组件Concrete Component是被装饰的原始对象它定义了需要添加新功能的对象。抽象装饰器Decorator继承自抽象组件它包含了一个抽象组件对象并定义了与抽象组件相同的接口同时可以通过组合方式持有其他装饰器对象。具体装饰器Concrete Decorator实现了抽象装饰器的接口负责向抽象组件添加新的功能。具体装饰器通常会在调用原始对象的方法之前或之后执行自己的操作。
装饰器模式通过嵌套包装多个装饰器对象可以实现多层次的功能增强。每个具体装饰器类都可以选择性地增加新的功能同时保持对象接口的一致性。
优缺点
优点
装饰类和被装饰类可以独立发展不会相互耦合装饰模式是继承的一个替代模式装饰模式可以动态扩展一个实现类的功能。可以轻量级的扩展被装饰对象的功能
缺点
多层装饰比较复杂。
代码实现 python代码
python原生支持装饰器并且提供符语法糖来简化装饰器的使用包括classmethodcached…都是python内置的装饰器。
def log(func):def wrapper(*args, **kw):print call %s(): % func.__name__return func(*args, **kw)return wrapperlog
def now():print 2016-12-04
if __name____main__:now()打印如下
call now():
2016-12-04go代码
go的装饰器可能不会像python一样随心所欲但这不代表你不可以使用装饰器只是需要更加具体一些比如实现一个中间件
package mainimport (fmtlognet/http
)func isAuthorized(endpoint func(http.ResponseWriter, *http.Request)) http.Handler {return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {fmt.Println(Checking to see if Authorized header set...)if val, ok : r.Header[Authorized]; ok {fmt.Println(val)if val[0] true {fmt.Println(Header is set! We can serve content!)endpoint(w, r)}} else {fmt.Println(Not Authorized!!)fmt.Fprintf(w, Not Authorized!!)}})
}func homePage(w http.ResponseWriter, r *http.Request) {fmt.Println(Endpoint Hit: homePage)fmt.Fprintf(w, Welcome to the HomePage!)
}func handleRequests() {http.Handle(/, isAuthorized(homePage))log.Fatal(http.ListenAndServe(:8081, nil))
}func main() {handleRequests()
}这突出了装饰器模式的主要优点在我们的代码库中包装代码非常简单。
适配器模式
适配器模式Adapter Pattern是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式它结合了两个独立接口的功能。
这种模式涉及到一个单一的类该类负责加入独立的或不兼容的接口功能。举个真实的例子读卡器是作为内存卡和笔记本之间的适配器。将内存卡插入读卡器再将读卡器插入笔记本这样就可以通过笔记本来读取内存卡。
使用场景
意图**将一个类的接口转换成客户希望的另外一个接口。**适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
主要解决主要解决在软件系统中常常要将一些现存的对象放到新的环境中而新环境要求的接口是现对象不能满足的。
何时使用 1、系统需要使用现有的类而此类的接口不符合系统的需要。 2、想要建立一个可以重复使用的类用于与一些彼此之间没有太大关联的一些类包括一些可能在将来引进的类一起工作这些源类不一定有一致的接口。 3、通过接口转换将一个类插入另一个类系中。比如老虎和飞禽现在多了一个飞虎在不增加实体的需求下增加一个适配器在里面包容一个虎对象实现飞的接口。
如何解决继承或依赖推荐。
关键代码适配器继承或依赖已有的对象实现想要的目标接口。
应用实例 1、美国电器 110V中国 220V就要有一个适配器将 110V 转化为 220V。 2、JAVA JDK 1.1 提供了 Enumeration 接口而在 1.2 中提供了 Iterator 接口想要使用 1.2 的 JDK则要将以前系统的 Enumeration 接口转化为 Iterator 接口这时就需要适配器模式。 3、在 LINUX 上运行 WINDOWS 程序。
使用场景有动机地修改一个正常运行的系统的接口这时应该考虑使用适配器模式。
注意事项适配器不是在详细设计时添加的而是解决正在服役的项目的问题。
优缺点
优点
1、可以让任何两个没有关联的类一起运行。 2、提高了类的复用。 3、增加了类的透明度。 4、灵活性好。
缺点 过多地使用适配器会让系统非常零乱不易整体进行把握。比如明明看到调用的是 A 接口其实内部被适配成了 B 接口的实现一个系统如果太多出现这种情况无异于一场灾难。因此如果不是很有必要可以不使用适配器而是直接对系统进行重构。 适配器模式与原配接口相比毕竟增加了一层调用关系所以在设计系统时不要使用适配器模式。
代码实现
假设有一个 MediaPlayer 接口和一个实现了 MediaPlayer 接口的实体类 AudioPlayer。默认情况下AudioPlayer 可以播放 mp3 格式的音频文件。
还有另一个接口 AdvancedMediaPlayer 和实现了 AdvancedMediaPlayer 接口的实体类。该类可以播放 vlc 和 mp4 格式的文件。
想要让 AudioPlayer 播放其他格式的音频文件。为了实现这个功能需要创建一个实现了 MediaPlayer 接口的适配器类 MediaAdapter并使用 AdvancedMediaPlayer 对象来播放所需的格式。
AudioPlayer 使用适配器类 MediaAdapter 传递所需的音频类型不需要知道能播放所需格式音频的实际类。AdapterPatternDemo 类使用 AudioPlayer 类来播放各种格式。 python代码
假设某公司A与某公司B需要合作公司A需要访问公司B的人员信息但公司A与公司B协议接口不同该如何处理先将公司A和公司B针对各自的人员信息访问系统封装了对象接口。
class ACpnStaff:nameidphonedef __init__(self,id):self.ididdef getName(self):print A protocol getName method...id:%s%self.idreturn self.namedef setName(self,name):print A protocol setName method...id:%s%self.idself.namenamedef getPhone(self):print A protocol getPhone method...id:%s%self.idreturn self.phonedef setPhone(self,phone):print A protocol setPhone method...id:%s%self.idself.phonephone
class BCpnStaff:nameidtelephonedef __init__(self,id):self.ididdef get_name(self):print B protocol get_name method...id:%s%self.idreturn self.namedef set_name(self,name):print B protocol set_name method...id:%s%self.idself.namenamedef get_telephone(self):print B protocol get_telephone method...id:%s%self.idreturn self.telephonedef set_telephone(self,telephone):print B protocol get_name method...id:%s%self.idself.telephonetelephone为在A公司平台复用B公司接口直接调用B公司人员接口是个办法但会对现在业务流程造成不确定的风险。为减少耦合规避风险我们需要一个帮手就像是转换电器电压的适配器一样这个帮手就是协议和接口转换的适配器。适配器构造如下
class CpnStaffAdapter:b_cpndef __init__(self,id):self.b_cpnBCpnStaff(id)def getName(self):return self.b_cpn.get_name()def getPhone(self):return self.b_cpn.get_telephone()def setName(self,name):self.b_cpn.set_name(name)def setPhone(self,phone):self.b_cpn.set_telephone(phone)适配器将B公司人员接口封装而对外接口形式与A公司人员接口一致达到用A公司人员接口访问B公司人员信息的效果。
业务示例如下
if __name____main__:acpn_staffACpnStaff(123)acpn_staff.setName(X-A)acpn_staff.setPhone(10012345678)print A Staff Name:%s%acpn_staff.getName()print A Staff Phone:%s%acpn_staff.getPhone()bcpn_staffCpnStaffAdapter(456)bcpn_staff.setName(Y-B)bcpn_staff.setPhone(99987654321)print B Staff Name:%s%bcpn_staff.getName()print B Staff Phone:%s%bcpn_staff.getPhone()go代码
package maintype ACompany interface {GetName() string
}type ACompanyStaff struct {name string
}func (a *ACompanyStaff) GetName() string {return a.name
}type BCompanyStaff struct {name string
}func (b *BCompanyStaff) GetStaffName() string {return b.name
}type CompanyStaffAdapter struct {b *BCompanyStaff
}func (c *CompanyStaffAdapter) GetName() string {return c.b.name
}func main() {}门面模式/外观模式/Facade Pattern
外观模式Facade Pattern隐藏系统的复杂性并向客户端提供了一个客户端可以访问系统的接口。这种类型的设计模式属于结构型模式它向现有的系统添加一个接口来隐藏系统的复杂性。
这种模式涉及到一个单一的类该类提供了客户端请求的简化方法和对现有系统类方法的委托调用。
使用场景
意图为子系统中的一组接口提供一个一致的界面外观模式定义了一个高层接口这个接口使得这一子系统更加容易使用。
主要解决降低访问复杂系统的内部子系统时的复杂度简化客户端之间的接口。
何时使用 1、客户端不需要知道系统内部的复杂联系整个系统只需提供一个接待员即可。 2、定义系统的入口。
如何解决客户端不与系统耦合外观类与系统耦合。
关键代码在客户端和复杂系统之间再加一层这一层将调用顺序、依赖关系等处理好。
使用场景
为复杂的模块或子系统提供外界访问的模块。子系统相对独立。预防低水平人员带来的风险。
注意事项在层次化结构中可以使用外观模式定义系统中每一层的入口。
外观模式涉及以下核心角色 外观Facade:提供一个简化的接口封装了系统的复杂性。外观模式的客户端通过与外观对象交互而无需直接与系统的各个组件打交道。 子系统Subsystem:由多个相互关联的类组成负责系统的具体功能。外观对象通过调用这些子系统来完成客户端的请求。 客户端Client:使用外观对象来与系统交互而不需要了解系统内部的具体实现。
优缺点
优点 1、减少系统相互依赖。 2、提高灵活性。 3、提高了整体系统的安全性封装起的系统对外的接口才可以用隐藏了很多内部接口细节若方法不允许使用则在门面中可以进行灵活控制。
缺点门面模式的缺点在于不符合开闭原则一旦系统成形后需要修改几乎只能重写门面代码这比继承或者覆写等方式或者其它一些符合开闭原则的模式风险都会大一些。
代码实现 假设有一组火警报警系统由三个子元件构成一个警报器一个喷水器一个自动拨打电话的装置。
python代码
class AlarmSensor:def run(self):print Alarm Ring...
class WaterSprinker:def run(self):print Spray Water...
class EmergencyDialer:def run(self):print Dial 119...在业务中如果需要将三个部件启动例如如果有一个烟雾传感器检测到了烟雾。在业务环境中需要做如下操作
if __name____main__:alarm_sensorAlarmSensor()water_sprinkerWaterSprinker()emergency_dialerEmergencyDialer()alarm_sensor.run()water_sprinker.run()emergency_dialer.run()但如果在多个业务场景中需要启动三个部件怎么办需要将其进行封装在设计模式中被封装成的新对象叫做门面。门面构建如下
class EmergencyFacade:def __init__(self):self.alarm_sensorAlarmSensor()self.water_sprinkerWaterSprinker()self.emergency_dialerEmergencyDialer()def runAll(self):self.alarm_sensor.run()self.water_sprinker.run()self.emergency_dialer.run()if __name____main__:emergency_facadeEmergencyFacade()emergency_facade.runAll()go代码
package maintype AlarmSensor struct {
}func (al *AlarmSensor) run() {print(Alarm Ring...)
}type WaterSprinker struct {
}func (ws *WaterSprinker) run() {print(Spray Water...)
}type EmergencyDialer struct {
}func (ed *EmergencyDialer) run() {print(Dial 119...)
}type EmergencyFacade struct {al *AlarmSensorws *WaterSprinkered *EmergencyDialer
}func (face *EmergencyFacade) Run() {face.al.run()face.ws.run()face.ed.run()
}func main() {}组合模式
组合模式Composite Pattern又叫部分整体模式是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象用来表示部分以及整体层次。这种类型的设计模式属于结构型模式它创建了对象组的树形结构。
这种模式创建了一个包含自己对象组的类。该类提供了修改相同对象组的方式。
使用场景
意图将对象组合成树形结构以表示部分-整体的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
主要解决它在我们树型结构的问题中模糊了简单元素和复杂元素的概念客户程序可以像处理简单元素一样来处理复杂元素从而使得客户程序与复杂元素的内部结构解耦。
何时使用 1、您想表示对象的部分-整体层次结构树形结构。 2、您希望用户忽略组合对象与单个对象的不同用户将统一地使用组合结构中的所有对象。
如何解决树枝和叶子实现统一接口树枝内部组合该接口。
关键代码树枝内部组合该接口并且含有内部属性 List里面放 Component。
使用场景部分、整体场景如树形菜单文件、文件夹的管理。
注意事项定义时为具体类。
组合模式的核心角色包括 组件Component:定义了组合中所有对象的通用接口可以是抽象类或接口。它声明了用于访问和管理子组件的方法包括添加、删除、获取子组件等。 叶子节点Leaf:表示组合中的叶子节点对象叶子节点没有子节点。它实现了组件接口的方法但通常不包含子组件。 复合节点Composite:表示组合中的复合对象复合节点可以包含子节点可以是叶子节点也可以是其他复合节点。它实现了组件接口的方法包括管理子组件的方法。 客户端Client:通过组件接口与组合结构进行交互客户端不需要区分叶子节点和复合节点可以一致地对待整体和部分。
优缺点
优点 1、高层模块调用简单。 2、节点自由增加。
缺点在使用组合模式时其叶子和树枝的声明都是实现类而不是接口违反了依赖倒置原则。
代码实现
假设有一个类 Employee该类被当作组合模型类。CompositePatternDemo 类使用 Employee 类来添加部门层次结构并打印所有员工。 python代码
class Employee:def __init__(self, name: str, subordinate: list[Employee] None):self.name nameself.subordinate [] if subordinate is None else subordinatedef add(self, employee: Employee):self.subordinate.append(employee)def remove(self, employee: Employee):self.subordinate.remove(employee)def get_subordinate(self):return [sub.name for sub in self.subordinate]if __name__ __main__:CEO Employee(generalzy)dog1 Employee(dg1)dog2 Employee(dg2)CEO.add(dog1)dog1.add(dog2)print(CEO.get_subordinate())CEO.remove(dog1)print(CEO.get_subordinate())执行程序输出结果为
[dg1]
[]go代码
package mainimport fmttype File struct {name stringcontent []bytefiles []*File
}func NewFile(name string) *File {return File{name: name,content: nil,files: nil,}
}func (f *File) Add(file *File) {if f.files nil {f.files make([]*File, 0, 1)}f.files append(f.files, file)
}func (f *File) Remove(file *File) {if f.files nil {f.files make([]*File, 0, 1)}idx : 0for index : 0; index len(f.files); index {if f.files[index] file {idx indexbreak}}f.files append(f.files[:idx], f.files[idx1:]...)
}func (f *File) GetFiles() []string {r : make([]string, 0, len(f.files))for _, file : range f.files {r append(r, file.name)}return r
}func main() {f : NewFile(dir1)f.Add(NewFile(dir2))fmt.Println(f.GetFiles())f.Remove(NewFile(file1))fmt.Println(f.GetFiles())
}[dir2]
[]享元模式
享元模式Flyweight Pattern主要用于减少创建对象的数量以减少内存占用和提高性能。这种类型的设计模式属于结构型模式它提供了减少对象数量从而改善应用所需的对象结构的方式。
享元模式尝试重用现有的同类对象如果未找到匹配的对象则创建新对象。
使用场景
意图运用共享技术有效地支持大量细粒度的对象。
主要解决在有大量对象时有可能会造成内存溢出我们把其中共同的部分抽象出来如果有相同的业务请求直接返回在内存中已有的对象避免重新创建。
何时使用 1、系统中有大量对象。 2、这些对象消耗大量内存。 3、这些对象的状态大部分可以外部化。 4、这些对象可以按照内蕴状态分为很多组当把外蕴对象从对象中剔除出来时每一组对象都可以用一个对象来代替。 5、系统不依赖于这些对象身份这些对象是不可分辨的。
如何解决用唯一标识码判断如果在内存中有则返回这个唯一标识码所标识的对象。
使用场景 1、系统有大量相似对象。 2、需要缓冲池的场景。
关键代码用 HashMap 存储这些对象。
注意事项 1、注意划分外部状态和内部状态否则可能会引起线程安全问题。 2、这些类必须有一个工厂对象加以控制。
享元模式包含以下几个核心角色 享元工厂Flyweight Factory:负责创建和管理享元对象通常包含一个池缓存用于存储和复用已经创建的享元对象。 具体享元Concrete Flyweight:实现了抽象享元接口包含了内部状态和外部状态。内部状态是可以被共享的而外部状态则由客户端传递。 抽象享元Flyweight:定义了具体享元和非共享享元的接口通常包含了设置外部状态的方法。 客户端Client:使用享元工厂获取享元对象并通过设置外部状态来操作享元对象。客户端通常不需要关心享元对象的具体实现。
优缺点
优点大大减少对象的创建降低系统的内存使效率提高。
缺点
提高了系统的复杂度需要分离出外部状态和内部状态而且外部状态具有固有化的性质不应该随着内部状态的变化而变化否则会造成系统的混乱。享元模式中需要额外注意线程安全问题。
代码实现
创建一个 Shape 接口和实现了 Shape 接口的实体类 Circle。下一步是定义工厂类 ShapeFactory。
ShapeFactory 有一个 Circle 的 HashMap其中键名为 Circle 对象的颜色。无论何时接收到请求都会创建一个特定颜色的圆。ShapeFactory 检查它的 HashMap 中的 circle 对象如果找到 Circle 对象则返回该对象否则将创建一个存储在 hashmap 中以备后续使用的新对象并把该对象返回到客户端。
FlyWeightPatternDemo 类使用 ShapeFactory 来获取 Shape 对象。它将向 ShapeFactory 传递信息red / green / blue/ black / white以便获取它所需对象的颜色。 python代码
class Shape:def draw(self):raise NotImplementedError()class Circle(Shape):def __init__(self, color: str):self.color colordef draw(self):print(fcreate a {self.color} circle)class ShapeFactory:__circle_map__: dict[str, Circle] dict()def draw_circle(self, color: str):if circle : self.__circle_map__.get(color):return circle.draw()c Circle(color)c.draw()self.__circle_map__[color] cif __name__ __main__:factory ShapeFactory()for i in range(20):factory.draw_circle()go代码
type Image struct {Name stringData string
}type ImageFactory struct {images map[string]*Imagelock sync.Mutex
}func NewImageFactory() *ImageFactory {return ImageFactory{images: make(map[string]*Image),}
}func (factory *ImageFactory) GetImage(name string) *Image {factory.lock.Lock()defer factory.lock.Unlock()if img, ok : factory.images[name]; ok {return img}img : Image{Name: name,Data: fmt.Sprintf(data of %s, name),}factory.images[name] imgreturn img
}func (factory *ImageFactory) PutImage(img *Image) {factory.lock.Lock()defer factory.lock.Unlock()factory.images[img.Name] img
}对于池化的概念go语言默认提供了sync.Pool可以更容易实现一个对象的复用
package mainimport (fmtsync
)// Image 表示图像对象
type Image struct {Name stringData []byte
}// ImageFactory 表示图像工厂用于创建和缓存图像对象
type ImageFactory struct {pool sync.Pool
}// NewImageFactory 创建一个新的图像工厂
func NewImageFactory() *ImageFactory {factory : ImageFactory{}factory.pool.New func() interface{} {return Image{}}return factory
}// GetImage 根据名称获取图像对象如果对象池中存在相同名称的对象则直接返回否则创建一个新对象并将其放入对象池中
func (factory *ImageFactory) GetImage(name string) *Image {obj : factory.pool.Get().(*Image)if obj.Name ! name {fmt.Println(Creating new image object)obj.Name nameobj.Data []byte(fmt.Sprintf(Image data for %s, name))}return obj
}// PutImage 将使用完的图像对象放回对象池中以供下次使用
func (factory *ImageFactory) PutImage(img *Image) {factory.pool.Put(img)
}func main() {factory : NewImageFactory()img1 : factory.GetImage(image1)img2 : factory.GetImage(image2)img3 : factory.GetImage(image1)fmt.Println(img1.Data)fmt.Println(img2.Data)fmt.Println(img3.Data)factory.PutImage(img1)factory.PutImage(img2)factory.PutImage(img3)
}桥接模式
桥接Bridge是用于把抽象化与实现化解耦使得二者可以独立变化。这种类型的设计模式属于结构型模式它通过提供抽象化和实现化之间的桥接结构来实现二者的解耦。
这种模式涉及到一个作为桥接的接口使得实体类的功能独立于接口实现类这两种类型的类可被结构化改变而互不影响。
桥接模式的目的是将抽象与实现分离使它们可以独立地变化该模式通过将一个对象的抽象部分与它的实现部分分离使它们可以独立地改变。它通过组合的方式而不是继承的方式将抽象和实现的部分连接起来。
使用场景
意图将抽象部分与实现部分分离使它们都可以独立的变化。
主要解决在有多种可能会变化的情况下用继承会造成类爆炸问题扩展起来不灵活。
何时使用实现系统可能有多个角度分类每一种角度都可能变化。
如何解决把这种多角度分类分离出来让它们独立变化减少它们之间耦合。
关键代码抽象类依赖实现类。
使用场景 1、如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性避免在两个层次之间建立静态的继承联系通过桥接模式可以使它们在抽象层建立一个关联关系。
2、对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统桥接模式尤为适用。
3、一个类存在两个独立变化的维度且这两个维度都需要进行扩展。
注意事项对于两个独立变化的维度使用桥接模式再适合不过了。
以下是桥接模式的几个关键角色
抽象Abstraction定义抽象接口通常包含对实现接口的引用。 扩展抽象Refined Abstraction对抽象的扩展可以是抽象类的子类或具体实现类。 实现Implementor定义实现接口提供基本操作的接口。 具体实现Concrete Implementor实现实现接口的具体类。
优缺点
优点 1、抽象和实现的分离。 2、优秀的扩展能力。 3、实现细节对客户透明。
缺点桥接模式的引入会增加系统的理解与设计难度由于聚合关联关系建立在抽象层要求开发者针对抽象进行设计与编程。
代码实现
有一个作为桥接实现的 DrawAPI 接口和实现了 DrawAPI 接口的实体类 RedCircle、GreenCircle。Shape 是一个抽象类将使用 DrawAPI 的对象。BridgePatternDemo 类使用 Shape 类来画出不同颜色的圆。 python代码
class DrawApi:def draw_circle(self, x: int, y: int, radius: int):raise NotImplementedError()class RedCircle(DrawApi):def draw_circle(self, x: int, y: int, radius: int):print(fcreate a red circle which radius is {radius} at ({x},{y}))class GreenCircle(DrawApi):def draw_circle(self, x: int, y: int, radius: int):print(fcreate a green circle which radius is {radius} at ({x},{y}))class ShapeInterface:def __init__(self, api: DrawApi):self.draw_api apidef draw(self):raise NotImplementedError()class Circle(ShapeInterface):def __init__(self, x: int, y: int, radius: int, api: DrawApi):super().__init__(api)self.x xself.y yself.radius radiusdef draw(self):self.draw_api.draw_circle(self.x, self.y, self.radius)if __name__ __main__:red Circle(1, 2, 3, RedCircle())green Circle(4, 5, 6, GreenCircle())red.draw()green.draw()执行结果
create a red circle which radius is 3 at (1,2)
create a green circle which radius is 6 at (4,5)go代码
package mainimport fmt/*** Description: 消息发送接口*/
type MessageSend interface {send(msg string)
}/*** Description: 短信消息*/
type SMS struct {
}func (s *SMS) send(msg string) {fmt.Println(sms 发送的消息内容为: msg)
}/*** Description: 邮件消息*/
type Email struct {
}func (e *Email) send(msg string) {fmt.Println(email 发送的消息内容为: msg)
}/*** Description: AppPush消息*/
type AppPush struct {
}func (a *AppPush) send(msg string) {fmt.Println(appPush 发送的消息内容为: msg)
}/*** Description: 站内信消息*/
type Letter struct {
}func (l *Letter) send(msg string) {fmt.Println(站内信 发送的消息内容为: msg)
}/*** Description: 用户触达父类包含触达方式数组messageSends*/
type Touch struct {messageSends []MessageSend
}/*** Description: 触达方法调用每一种方式进行触达* receiver t* param msg*/
func (t *Touch) do(msg string) {for _, s : range t.messageSends {s.send(msg)}
}/*** Description: 紧急消息做用户触达*/
type TouchUrgent struct {base Touch
}/*** Description: 紧急消息先从db中获取各种信息然后使用各种触达方式通知用户* receiver t* param msg*/
func (t *TouchUrgent) do(msg string) {fmt.Println(touch urgent 从db获取接收人等信息)t.base.do(msg)
}/*** Description: 普通消息做用户触达*/
type TouchNormal struct {base Touch
}/*** Description: 普通消息先从文件中获取各种信息然后使用各种触达方式通知用户* receiver t* param msg*/
func (t *TouchNormal) do(msg string) {fmt.Println(touch normal 从文件获取接收人等信息)t.base.do(msg)
}func main() {//触达方式sms : SMS{}appPush : AppPush{}letter : Letter{}email : Email{}//根据触达类型选择触达方式fmt.Println(-------------------touch urgent)touchUrgent : TouchUrgent{base: Touch{messageSends: []MessageSend{sms, appPush, letter, email},},}touchUrgent.do(urgent情况)fmt.Println(-------------------touch normal)touchNormal : TouchNormal{ //base: Touch{messageSends: []MessageSend{sms, appPush, letter, email},},}touchNormal.do(normal情况)
}行为类设计模式11种
策略模式
在策略模式Strategy Pattern中一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。
在策略模式定义了一系列算法或策略并将每个算法封装在独立的类中使得它们可以互相替换。通过使用策略模式可以在运行时根据需要选择不同的算法而不需要修改客户端代码。
在策略模式中我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。
使用场景
意图定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。
主要解决在有多种算法相似的情况下使用 if…else 所带来的复杂和难以维护。
何时使用一个系统有许多许多类而区分它们的只是他们直接的行为。
如何解决将这些算法封装成一个一个的类任意地替换。
关键代码实现同一个接口。
使用场景 1、如果在一个系统里面有许多类它们之间的区别仅在于它们的行为那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。 2、一个系统需要动态地在几种算法中选择一种。 3、如果一个对象有很多的行为如果不用恰当的模式这些行为就只好使用多重的条件选择语句来实现。
注意事项如果一个系统的策略多于四个就需要考虑使用混合模式解决策略类膨胀的问题。
策略模式包含以下几个核心角色
环境Context维护一个对策略对象的引用负责将客户端请求委派给具体的策略对象执行。环境类可以通过依赖注入、简单工厂等方式来获取具体策略对象。抽象策略Abstract Strategy定义了策略对象的公共接口或抽象类规定了具体策略类必须实现的方法。具体策略Concrete Strategy实现了抽象策略定义的接口或抽象类包含了具体的算法实现。
策略模式通过将算法与使用算法的代码解耦提供了一种动态选择不同算法的方法。客户端代码不需要知道具体的算法细节而是通过调用环境类来使用所选择的策略。
优缺点
优点 1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。
缺点 1、策略类会增多。 2、所有策略类都需要对外暴露。
代码实现
创建一个定义活动的 Strategy 接口和实现了 Strategy 接口的实体策略类。Context 是一个使用了某种策略的类。
StrategyPatternDemo我们的演示类使用 Context 和策略对象来演示 Context 在它所配置或使用的策略改变时的行为变化。 python代码
# 不是原神...
class Op:def do_op(self, a: int, b: int) - int:raise NotImplementedError()class OpADD(Op):def do_op(self, a: int, b: int) - int:return a bclass OpSubtract(Op):def do_op(self, a: int, b: int) - int:return a - bclass Context:def __init__(self, op: Op):self.op opdef exe(self, a: int, b: int) - int:return self.op.do_op(a, b)if __name__ __main__:ctx Context(OpADD())print(ctx.exe(1, 2))ctx Context(OpSubtract())print(ctx.exe(1, 3))执行结果
3
-2go代码
package maintype Op interface {do(int, int) int
}type AddOp struct {
}func (add *AddOp) do(a, b int) int {return a b
}type SubOp struct {
}func (sub *SubOp) do(a, b int) int {return a - b
}type Context struct {op Op
}func (ctx *Context) Exe(a, b int) int {return ctx.op.do(a, b)
}func main() {ctx : Context{op: new(AddOp)}println(ctx.Exe(1, 2))ctx Context{op: new(SubOp)}println(ctx.Exe(1, 5))
}执行结果
3
-4责任链模式
顾名思义责任链模式Chain of Responsibility Pattern为请求创建了一个接收者对象的链。这种模式给予请求的类型对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。
在这种模式中通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求那么它会把相同的请求传给下一个接收者依此类推。
使用场景
意图避免请求发送者与接收者耦合在一起让多个对象都有可能接收请求将这些对象连接成一条链并且沿着这条链传递请求直到有对象处理它为止。
主要解决职责链上的处理者负责处理请求客户只需要将请求发送到职责链上即可无须关心请求的处理细节和请求的传递所以职责链将请求的发送者和请求的处理者解耦了。
何时使用在处理消息的时候以过滤很多道。
如何解决拦截的类都实现统一接口。
关键代码Handler 里面聚合它自己在 HandlerRequest 里判断是否合适如果没达到条件则向下传递向谁传递之前 set 进去。
使用场景 1、有多个对象可以处理同一个请求具体哪个对象处理该请求由运行时刻自动确定。 2、在不明确指定接收者的情况下向多个对象中的一个提交一个请求。 3、可动态指定一组对象处理请求。
主要涉及到以下几个核心角色 抽象处理者Handler:定义一个处理请求的接口通常包含一个处理请求的方法如 handleRequest和一个指向下一个处理者的引用后继者。 具体处理者ConcreteHandler:实现了抽象处理者接口负责处理请求。如果能够处理该请求则直接处理否则将请求传递给下一个处理者。 客户端Client:创建处理者对象并将它们连接成一条责任链。通常客户端只需要将请求发送给责任链的第一个处理者无需关心请求的具体处理过程。
优缺点
优点 1、降低耦合度。它将请求的发送者和接收者解耦。 2、简化了对象。使得对象不需要知道链的结构。 3、增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序允许动态地新增或者删除责任。 4、增加新的请求处理类很方便。
缺点 1、不能保证请求一定被接收。 2、系统性能将受到一定影响而且在进行代码调试时不太方便可能会造成循环调用。 3、可能不容易观察运行时的特征有碍于除错。
代码实现
创建抽象类 AbstractLogger带有详细的日志记录级别。然后我们创建三种类型的记录器都扩展了 AbstractLogger。每个记录器消息的级别是否属于自己的级别如果是则相应地打印出来否则将不打印并把消息传给下一个记录器。 python代码
INFO: int 1
ERROR: int 3class Logger:level: int -1next: Logger Nonedef write(self, content: str) - None:raise NotImplementedError()def log_message(self, level: int, content: str):if self.level level:self.write(contentcontent)if self.next is not None:self.next.log_message(level, content)class InfoLogger(Logger):level INFOdef write(self, content: str) - None:print(f[INFO] {content})class ErrorLogger(Logger):level ERRORdef write(self, content: str) - None:print(f[ERROR] {content})def get_logger():error ErrorLogger()info InfoLogger()info.next errorreturn infoif __name__ __main__:logger get_logger()logger.log_message(ERROR, hello world)go代码
package mainimport fmtconst (INFO iotaERROR
)type Logger interface {Log(int, string)Next() Logger
}type BaseLogger struct {level intnext Logger
}func (bl *BaseLogger) Next() Logger {return bl.next
}func (bl *BaseLogger) Log(level int, content string) {panic(not implement)
}type ErrorLogger struct {BaseLogger
}func (e *ErrorLogger) Log(level int, content string) {if e.level level {fmt.Println([ERROR], content)}if next : e.Next(); next ! nil {next.Log(level, content)}
}type InFoLogger struct {BaseLogger
}func (e *InFoLogger) Log(level int, content string) {if e.level level {fmt.Println([INFO], content)}if next : e.Next(); next ! nil {next.Log(level, content)}
}func getLogger() Logger {info : InFoLogger{BaseLogger{level: INFO, next: nil}}err : ErrorLogger{BaseLogger{level: ERROR, next: nil}}info.next errreturn info
}func main() {l : getLogger()l.Log(ERROR, hello world)
}go语言中匿名结构体的嵌套也可以实现类似其他语言中的继承关系。但go更赞同的是组合的概念而不是继承关系。
命令模式
命令模式Command Pattern是一种数据驱动的设计模式它属于行为型模式。请求以命令的形式包裹在对象中并传给调用对象。调用对象寻找可以处理该命令的合适的对象并把该命令传给相应的对象该对象执行命令。
使用场景
意图将一个请求封装成一个对象从而使您可以用不同的请求对客户进行参数化。
主要解决在软件系统中行为请求者与行为实现者通常是一种紧耦合的关系但某些场合比如需要对行为进行记录、撤销或重做、事务等处理时这种无法抵御变化的紧耦合的设计就不太合适。
如何解决通过调用者调用接受者执行命令顺序调用者→命令→接受者。
关键代码定义三个角色1、received 真正的命令执行对象 2、Command 3、invoker 使用命令对象的入口
应用实例struts 1 中的 action 核心控制器 ActionServlet 只有一个相当于 Invoker而模型层的类会随着不同的应用有不同的模型类相当于具体的 Command。
使用场景认为是命令的地方都可以使用命令模式比如 1、GUI 中每一个按钮都是一条命令。 2、模拟 CMD。
注意事项系统需要支持命令的撤销Undo操作和恢复Redo操作也可以考虑使用命令模式见命令模式的扩展。
命令模式结构示意图: 主要涉及到以下几个核心角色 命令Command:定义了执行操作的接口通常包含一个 execute 方法用于调用具体的操作。 具体命令ConcreteCommand:实现了命令接口负责执行具体的操作。它通常包含了对接收者的引用通过调用接收者的方法来完成请求的处理。 接收者Receiver:知道如何执行与请求相关的操作实际执行命令的对象。 调用者/请求者Invoker:发送命令的对象它包含了一个命令对象并能触发命令的执行。调用者并不直接处理请求而是通过将请求传递给命令对象来实现。 客户端Client:创建具体命令对象并设置其接收者将命令对象交给调用者执行。
优缺点
优点 1、降低了系统耦合度。 2、新的命令可以很容易添加到系统中去。
缺点使用命令模式可能会导致某些系统有过多的具体命令类。
代码实现 一个点餐系统可以将该系统设计成前台服务员系统和后台系统后台系统进一步细分成主食子系统凉菜子系统热菜子系统。
python代码
后台三个子系统设计如下
class backSys():def cook(self,dish):pass
class mainFoodSys(backSys):def cook(self,dish):print MAINFOOD:Cook %s%dish
class coolDishSys(backSys):def cook(self,dish):print COOLDISH:Cook %s%dish
class hotDishSys(backSys):def cook(self,dish):print HOTDISH:Cook %s%dish前台服务员系统与后台系统的交互可以通过命令的模式来实现服务员将顾客的点单内容封装成命令直接对后台下达命令后台完成命令要求的事即可。前台系统构建如下
class waiterSys():menu_mapdict()commandList[]def setOrder(self,command):print WAITER:Add dishself.commandList.append(command)def cancelOrder(self,command):print WAITER:Cancel order...self.commandList.remove(command)def notify(self):print WAITER:Nofify...for command in self.commandList:command.execute()前台系统中的notify接口直接调用命令中的execute接口执行命令。命令类构建如下
class Command():receiver Nonedef __init__(self, receiver):self.receiver receiverdef execute(self):pass
class foodCommand(Command):dishdef __init__(self,receiver,dish):self.receiverreceiverself.dishdishdef execute(self):self.receiver.cook(self.dish)class mainFoodCommand(foodCommand):pass
class coolDishCommand(foodCommand):pass
class hotDishCommand(foodCommand):pass为使场景业务精简一些我们再加一个菜单类来辅助业务菜单类在本例中直接写死。
class menuAll:menu_mapdict()def loadMenu(self):#加载菜单这里直接写死self.menu_map[hot] [Yu-Shiang Shredded Pork, Sauteed Tofu, Home Style, Sauteed Snow Peas]self.menu_map[cool] [Cucumber, Preserved egg]self.menu_map[main] [Rice, Pie]def isHot(self,dish):if dish in self.menu_map[hot]:return Truereturn Falsedef isCool(self,dish):if dish in self.menu_map[cool]:return Truereturn Falsedef isMain(self,dish):if dish in self.menu_map[main]:return Truereturn False业务场景如下
if __name____main__:dish_list[Yu-Shiang Shredded Pork,Sauteed Tofu, Home Style,Cucumber,Rice]#顾客点的菜waiter_syswaiterSys()main_food_sysmainFoodSys()cool_dish_syscoolDishSys()hot_dish_syshotDishSys()menumenuAll()menu.loadMenu()for dish in dish_list:if menu.isCool(dish):cmdcoolDishCommand(cool_dish_sys,dish)elif menu.isHot(dish):cmdhotDishCommand(hot_dish_sys,dish)elif menu.isMain(dish):cmdmainFoodCommand(main_food_sys,dish)else:continuewaiter_sys.setOrder(cmd)waiter_sys.notify()打印如下
WAITER:Add dish
WAITER:Add dish
WAITER:Add dish
WAITER:Add dish
WAITER:Nofify...
HOTDISH:Cook Yu-Shiang Shredded Pork
HOTDISH:Cook Sauteed Tofu, Home Style
COOLDISH:Cook Cucumber
MAINFOOD:Cook Ricego代码
package mainimport fmttype BackSys interface {Cook(dish string)
}type MainFoodSys struct{}func (m *MainFoodSys) Cook(dish string) {fmt.Printf(MAINFOOD:Cook %s\n, dish)
}type CoolDishSys struct{}func (c *CoolDishSys) Cook(dish string) {fmt.Printf(COOLDISH:Cook %s\n, dish)
}type HotDishSys struct{}func (h *HotDishSys) Cook(dish string) {fmt.Printf(HOTDISH:Cook %s\n, dish)
}type WaiterSys struct {commandList []Command
}func (w *WaiterSys) SetOrder(command Command) {fmt.Println(WAITER:Add dish)w.commandList append(w.commandList, command)
}func (w *WaiterSys) CancelOrder(command Command) {fmt.Println(WAITER:Cancel order...)// Assuming Command interface has an IsEqual method to identify the command to removefor i, cmd : range w.commandList {if cmd.IsEqual(command) {w.commandList append(w.commandList[:i], w.commandList[i1:]...)break}}
}func (w *WaiterSys) Notify() {fmt.Println(WAITER:Notify...)for _, command : range w.commandList {command.Execute()}
}type Command interface {Execute()IsEqual(Command) bool
}type FoodCommand struct {receiver BackSysdish string
}func (f *FoodCommand) Execute() {f.receiver.Cook(f.dish)
}func (f *FoodCommand) IsEqual(command Command) bool {// Implement comparison logic based on the needsreturn false
}type MainFoodCommand struct {FoodCommand
}type CoolDishCommand struct {FoodCommand
}type HotDishCommand struct {FoodCommand
}type MenuAll struct {menuMap map[string][]string
}func (m *MenuAll) LoadMenu() {m.menuMap make(map[string][]string)m.menuMap[hot] []string{Yu-Shiang Shredded Pork, Sauteed Tofu, Home Style, Sauteed Snow Peas}m.menuMap[cool] []string{Cucumber, Preserved egg}m.menuMap[main] []string{Rice, Pie}
}func (m *MenuAll) IsHot(dish string) bool {for _, d : range m.menuMap[hot] {if d dish {return true}}return false
}func (m *MenuAll) IsCool(dish string) bool {for _, d : range m.menuMap[cool] {if d dish {return true}}return false
}func (m *MenuAll) IsMain(dish string) bool {for _, d : range m.menuMap[main] {if d dish {return true}}return false
}func main() {dishList : []string{Yu-Shiang Shredded Pork, Sauteed Tofu, Home Style, Cucumber, Rice}waiterSys : WaiterSys{}mainFoodSys : MainFoodSys{}coolDishSys : CoolDishSys{}hotDishSys : HotDishSys{}menu : MenuAll{}menu.LoadMenu()for _, dish : range dishList {var cmd Commandif menu.IsCool(dish) {cmd CoolDishCommand{FoodCommand{receiver: coolDishSys, dish: dish}}} else if menu.IsHot(dish) {cmd HotDishCommand{FoodCommand{receiver: hotDishSys, dish: dish}}} else if menu.IsMain(dish) {cmd MainFoodCommand{FoodCommand{receiver: mainFoodSys, dish: dish}}} else {continue}waiterSys.SetOrder(cmd)}waiterSys.Notify()
}中介者模式
中介者模式Mediator Pattern是用来降低多个对象和类之间的通信复杂性。这种模式提供了一个中介类该类通常处理不同类之间的通信并支持松耦合使代码易于维护。中介者模式属于行为型模式。
使用场景
意图用一个中介对象来封装一系列的对象交互中介者使各对象不需要显式地相互引用从而使其耦合松散而且可以独立地改变它们之间的交互。
主要解决对象与对象之间存在大量的关联关系这样势必会导致系统的结构变得很复杂同时若一个对象发生改变我们也需要跟踪与之相关联的对象同时做出相应的处理。
何时使用多个类相互耦合形成了网状结构。
如何解决将上述网状结构分离为星型结构。
关键代码对象 Colleague 之间的通信封装到一个类中单独处理。
使用场景 1、系统中对象之间存在比较复杂的引用关系导致它们之间的依赖关系结构混乱而且难以复用该对象。 2、想通过一个中间类来封装多个类中的行为而又不想生成太多的子类。
注意事项不应当在职责混乱的时候使用。
优缺点
优点 1、降低了类的复杂度将一对多转化成了一对一。 2、各个类之间的解耦。 3、符合迪米特原则。
缺点中介者会庞大变得复杂难以维护。
代码实现
通过聊天室实例来演示中介者模式。实例中多个用户可以向聊天室发送消息聊天室向所有的用户显示消息。我们将创建两个类 ChatRoom 和 User。User 对象使用 ChatRoom 方法来分享他们的消息。
MediatorPatternDemo我们的演示类使用 User 对象来显示他们之间的通信。
python代码
from datetime import datetime# 创建中介类
class ChatRoom:staticmethoddef show_message(user, message):print(f{datetime.now()} [{user.get_name()}] : {message})
# 创建 user 类
class User:def __init__(self, name):self.__name namedef get_name(self):return self.__namedef set_name(self, name):self.__name namedef send_message(self, message):ChatRoom.show_message(self, message)# Demo
if __name__ __main__:robert User(Robert)john User(John)robert.send_message(Hi! John!)john.send_message(Hello! Robert!)go代码
package mainimport (fmttime
)type ChatRoom struct{}func (cr *ChatRoom) ShowMessage(user *User, message string) {fmt.Printf(%s [%s] : %s\n, time.Now().Format(2006-01-02 15:04:05), user.GetName(), message)
}type User struct {name string
}func NewUser(name string) *User {return User{name: name}
}func (u *User) GetName() string {return u.name
}func (u *User) SetName(name string) {u.name name
}func (u *User) SendMessage(message string, chatRoom *ChatRoom) {chatRoom.ShowMessage(u, message)
}func main() {chatRoom : ChatRoom{}robert : NewUser(Robert)john : NewUser(John)robert.SendMessage(Hi! John!, chatRoom)john.SendMessage(Hello! Robert!, chatRoom)
}模板模式
在模板模式Template Pattern中一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现但调用将以抽象类中定义的方式进行。这种类型的设计模式属于行为型模式。
使用场景
意图定义一个操作中的算法的骨架而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
主要解决一些方法通用却在每一个子类都重新写了这一方法。
何时使用有一些通用的方法。
如何解决将这些通用算法抽象出来。
关键代码在抽象类实现其他步骤在子类实现。
使用场景 1、有多个子类共有的方法且逻辑相同。 2、重要的、复杂的方法可以考虑作为模板方法。
注意事项为防止恶意操作一般模板方法都加上 final 关键词。
优缺点
优点 1、封装不变部分扩展可变部分。 2、提取公共代码便于维护。 3、行为由父类控制子类实现。
缺点每一个不同的实现都需要一个子类来实现导致类的个数增加使得系统更加庞大。
代码实现
将创建一个定义操作的 Game 抽象类Cricket 和 Football 是扩展了 Game 的实体类它们重写了抽象类的方法。
python代码
from abc import ABC, abstractmethodclass Game(ABC):abstractmethoddef initialize(self):passabstractmethoddef start_play(self):passabstractmethoddef end_play(self):passdef play(self):self.initialize()self.start_play()self.end_play()class Cricket(Game):def end_play(self):print(Cricket Game Finished!)def initialize(self):print(Cricket Game Initialized! Start playing.)def start_play(self):print(Cricket Game Started. Enjoy the game!)class Football(Game):def end_play(self):print(Football Game Finished!)def initialize(self):print(Football Game Initialized! Start playing.)def start_play(self):print(Football Game Started. Enjoy the game!)if __name__ __main__:game Cricket()game.play()print()game Football()game.play()go代码
package mainimport fmttype Game interface {initialize()startPlay()endPlay()play()
}type BaseGame struct{}func (g *BaseGame) play(game Game) {game.initialize()game.startPlay()game.endPlay()
}type Cricket struct {BaseGame
}func (c *Cricket) initialize() {fmt.Println(Cricket Game Initialized! Start playing.)
}func (c *Cricket) startPlay() {fmt.Println(Cricket Game Started. Enjoy the game!)
}func (c *Cricket) endPlay() {fmt.Println(Cricket Game Finished!)
}type Football struct {BaseGame
}func (f *Football) initialize() {fmt.Println(Football Game Initialized! Start playing.)
}func (f *Football) startPlay() {fmt.Println(Football Game Started. Enjoy the game!)
}func (f *Football) endPlay() {fmt.Println(Football Game Finished!)
}func main() {cricket : Cricket{}cricket.play(cricket)fmt.Println()football : Football{}football.play(football)
}迭代器模式
迭代器模式的定义如下它提供一种方法访问一个容器对象中各个元素而又不需要暴露对象的内部细节。
使用场景
意图提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。
主要解决不同的方式来遍历整个整合对象。
何时使用遍历一个聚合对象。
如何解决把在元素之间游走的责任交给迭代器而不是聚合对象。
使用场景 1、访问一个聚合对象的内容而无须暴露它的内部表示。 2、需要为聚合对象提供多种遍历方式。 3、为遍历不同的聚合结构提供一个统一的接口。
注意事项迭代器模式就是分离了集合对象的遍历行为抽象出一个迭代器类来负责这样既可以做到不暴露集合的内部结构又可让外部代码透明地访问集合内部的数据。
优缺点
优点 1、它支持以不同的方式遍历一个聚合对象。 2、迭代器简化了聚合类。 3、在同一个聚合上可以有多个遍历。 4、在迭代器模式中增加新的聚合类和迭代器类都很方便无须修改原有代码。
缺点由于迭代器模式将存储数据和遍历数据的职责分离增加新的聚合类需要对应增加新的迭代器类类的个数成对增加这在一定程度上增加了系统的复杂性。
代码实现
在python中迭代器并不用举太多的例子因为python中的迭代器应用实在太多了不管是python还是其它很多的编程语言中实际上迭代器都已经纳入到了常用的库或者包中。
而且在当前也几乎没有人专门去开发一个迭代器而是直接去使用list、string、set、dict等python可迭代对象或者直接使用__iter__和next函数来实现迭代器。
if __name____main__:lst[hello Alice,hello Bob,hello Eve]lst_iteriter(lst)print lst_iterprint lst_iter.next()print lst_iter.next()print lst_iter.next()print lst_iter.next()python代码
class MyIter(object):def __init__(self, n):self.index 0self.n ndef __iter__(self):return selfdef __next__(self):if self.index self.n:value self.index ** 2self.index 1return valueelse:raise StopIteration()if __name__ __main__:x_square MyIter(10)for x in x_square:print(x)注意yield 主要用于生成器函数而 __iter__ 和 __next__ 主要用于创建自定义迭代器对象。生成器是一种特殊的迭代器而迭代器是实现了 __iter__ 和 __next__ 方法的对象。
go代码
go语言的bufio.NewScanner()就是一个迭代器的案例
package mainimport (bufiofmtlogos
)func main() {file, err : os.Open(./xx.txt)if err ! nil {log.Fatal(err)}defer file.Close()scanner : bufio.NewScanner(file)for scanner.Scan() {fmt.Println(scanner.Text())}if err : scanner.Err(); err ! nil {log.Fatal(err)}
}读取文件只需要调用Scan()即可加载到缓存中同时隐藏了复杂的文件操作流程
func (s *Scanner) Scan() bool {if s.done {return false}s.scanCalled true// Loop until we have a token.for {// See if we can get a token with what we already have.// If weve run out of data but have an error, give the split function// a chance to recover any remaining, possibly empty token.if s.end s.start || s.err ! nil {advance, token, err : s.split(s.buf[s.start:s.end], s.err ! nil)if err ! nil {if err ErrFinalToken {s.token tokens.done truereturn true}s.setErr(err)return false}if !s.advance(advance) {return false}s.token tokenif token ! nil {if s.err nil || advance 0 {s.empties 0} else {// Returning tokens not advancing input at EOF.s.emptiesif s.empties maxConsecutiveEmptyReads {panic(bufio.Scan: too many empty tokens without progressing)}}return true}}// We cannot generate a token with what we are holding.// If weve already hit EOF or an I/O error, we are done.if s.err ! nil {// Shut it down.s.start 0s.end 0return false}// Must read more data.// First, shift data to beginning of buffer if theres lots of empty space// or space is needed.if s.start 0 (s.end len(s.buf) || s.start len(s.buf)/2) {copy(s.buf, s.buf[s.start:s.end])s.end - s.starts.start 0}// Is the buffer full? If so, resize.if s.end len(s.buf) {// Guarantee no overflow in the multiplication below.const maxInt int(^uint(0) 1)if len(s.buf) s.maxTokenSize || len(s.buf) maxInt/2 {s.setErr(ErrTooLong)return false}newSize : len(s.buf) * 2if newSize 0 {newSize startBufSize}if newSize s.maxTokenSize {newSize s.maxTokenSize}newBuf : make([]byte, newSize)copy(newBuf, s.buf[s.start:s.end])s.buf newBufs.end - s.starts.start 0}// Finally we can read some input. Make sure we dont get stuck with// a misbehaving Reader. Officially we dont need to do this, but lets// be extra careful: Scanner is for safe, simple jobs.for loop : 0; ; {n, err : s.r.Read(s.buf[s.end:len(s.buf)])if n 0 || len(s.buf)-s.end n {s.setErr(ErrBadReadCount)break}s.end nif err ! nil {s.setErr(err)break}if n 0 {s.empties 0break}loopif loop maxConsecutiveEmptyReads {s.setErr(io.ErrNoProgress)break}}}
}访问者模式
在访问者模式Visitor Pattern中我们使用了一个访问者类它改变了元素类的执行算法。通过这种方式元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行为型模式。根据模式元素对象已接受访问者对象这样访问者对象就可以处理元素对象上的操作。封装一些作用于某种数据结构中的各元素的操作它可以在不改变数据结构的前提下定义于作用于这些元素的新操作。
使用场景
意图主要将数据结构与数据操作分离。
主要解决稳定的数据结构和易变的操作耦合问题。
何时使用需要对一个对象结构中的对象进行很多不同的并且不相关的操作而需要避免让这些操作污染这些对象的类使用访问者模式将这些封装到类中。
使用场景 1、对象结构中对象对应的类很少改变但经常需要在此对象结构上定义新的操作。 2、需要对一个对象结构中的对象进行很多不同的并且不相关的操作而需要避免让这些操作污染这些对象的类也不希望在增加新操作时修改这些类。
优缺点
优点 1、符合单一职责原则。 2、优秀的扩展性。 3、灵活性。
缺点 1、具体元素对访问者公布细节违反了迪米特原则。 2、具体元素变更比较困难。 3、违反了依赖倒置原则依赖了具体类没有依赖抽象。
代码实现
创建一个定义接受操作的 ComputerPart 接口。Keyboard、Mouse、Monitor 和 Computer 是实现了 ComputerPart 接口的实体类。我们将定义另一个接口 ComputerPartVisitor它定义了访问者类的操作。Computer 使用实体访问者来执行相应的动作。
VisitorPatternDemo演示类使用 Computer、ComputerPartVisitor 类来演示访问者模式的用法。
python代码
# Visitor Pattern with Python Code
from abc import abstractmethod,ABCMeta
# 定义一个表示元素Element的接口
class ComputerPart(metaclassABCMeta):abstractmethoddef accept(self, inComputerPartVisitor):pass
#创建阔爱站了ComputerPart的实体类
class Keyboard(ComputerPart):def accept(self, inComputerPartVisitor):inComputerPartVisitor.visitKeyboard(self)
class Monitor(ComputerPart):def accept(self, inComputerPartVisitor):inComputerPartVisitor.visitMonitor(self)
class Mouse(ComputerPart):def accept(self, inComputerPartVisitor):inComputerPartVisitor.visitMouse(self)
class Computer(ComputerPart):_parts []def __init__(self):self._parts.append(Mouse())self._parts.append(Keyboard())self._parts.append(Monitor())def accept(self, inComputerPartVisitor):for aPart in self._parts :aPart.accept(inComputerPartVisitor)inComputerPartVisitor.visitComputer(self)
# 定义一个表示访问者的接口
class ComputerPartVisitor(metaclassABCMeta):abstractmethoddef visitComputer(self,inComputer):passabstractmethoddef visitMouse(self,inMouse):passabstractmethoddef visitKeyboard(self,inKeyboard):passabstractmethoddef visitMonitor(self,inMonitor):pass
# 实现访问者接口的实体类
class ComputerPartDisplayVisitor(ComputerPartVisitor):def visitComputer(self,inComputer):print(Displaying {0}. Called in {1}.format(inComputer.__class__.__name__,self.__class__.__name__))def visitMouse(self,inMouse):print(Displaying {0}. Called in {1}.format(inMouse.__class__.__name__,self.__class__.__name__))def visitKeyboard(self,inKeyboard):print(Displaying {0}. Called in {1}.format(inKeyboard.__class__.__name__,self.__class__.__name__))def visitMonitor(self,inMonitor):print(Displaying {0}. Called in {1}.format(inMonitor.__class__.__name__,self.__class__.__name__))
# 调用输出
if __name__ __main__:aComputer Computer()aComputer.accept(ComputerPartDisplayVisitor())go代码
package mainimport fmttype ComputerPart interface {Accept(computerPartVisitor ComputerPartVisitor)
}type Keyboard struct{}
func (k *Keyboard) Accept(computerPartVisitor ComputerPartVisitor) {computerPartVisitor.VisitKeyboard(k)
}type Monitor struct{}
func (m *Monitor) Accept(computerPartVisitor ComputerPartVisitor) {computerPartVisitor.VisitMonitor(m)
}type Mouse struct{}
func (m *Mouse) Accept(computerPartVisitor ComputerPartVisitor) {computerPartVisitor.VisitMouse(m)
}type Computer struct {parts []ComputerPart
}
func NewComputer() *Computer {return Computer{parts: []ComputerPart{Mouse{}, Keyboard{}, Monitor{}},}
}
func (c *Computer) Accept(computerPartVisitor ComputerPartVisitor) {for _, part : range c.parts {part.Accept(computerPartVisitor)}computerPartVisitor.VisitComputer(c)
}type ComputerPartVisitor interface {VisitComputer(computer *Computer)VisitMouse(mouse *Mouse)VisitKeyboard(keyboard *Keyboard)VisitMonitor(monitor *Monitor)
}type ComputerPartDisplayVisitor struct{}
func (cpdv *ComputerPartDisplayVisitor) VisitComputer(computer *Computer) {fmt.Printf(Displaying %T. Called in %T\n, computer, cpdv)
}
func (cpdv *ComputerPartDisplayVisitor) VisitMouse(mouse *Mouse) {fmt.Printf(Displaying %T. Called in %T\n, mouse, cpdv)
}
func (cpdv *ComputerPartDisplayVisitor) VisitKeyboard(keyboard *Keyboard) {fmt.Printf(Displaying %T. Called in %T\n, keyboard, cpdv)
}
func (cpdv *ComputerPartDisplayVisitor) VisitMonitor(monitor *Monitor) {fmt.Printf(Displaying %T. Called in %T\n, monitor, cpdv)
}func main() {computer : NewComputer()computer.Accept(ComputerPartDisplayVisitor{})
}观察者模式
观察者模式是一种行为型设计模式它定义了一种一对多的依赖关系当一个对象的状态发生改变时其所有依赖者都会收到通知并自动更新。
当对象间存在一对多关系时则使用观察者模式Observer Pattern。比如当一个对象被修改时则会自动通知依赖它的对象。观察者模式属于行为型模式。
使用场景
意图定义对象间的一种一对多的依赖关系当一个对象的状态发生改变时所有依赖于它的对象都得到通知并被自动更新。
主要解决一个对象状态改变给其他对象通知的问题而且要考虑到易用和低耦合保证高度的协作。
何时使用一个对象目标对象的状态发生改变所有的依赖对象观察者对象都将得到通知进行广播通知。
使用场景
一个抽象模型有两个方面其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。一个对象的改变将导致其他一个或多个对象也发生改变而不知道具体有多少对象将发生改变可以降低对象之间的耦合度。一个对象必须通知其他对象而并不知道这些对象是谁。需要在系统中创建一个触发链A对象的行为将影响B对象B对象的行为将影响C对象……可以使用观察者模式创建一种链式触发机制。
观察者模式包含以下几个核心角色
主题Subject也称为被观察者或可观察者它是具有状态的对象并维护着一个观察者列表。主题提供了添加、删除和通知观察者的方法。观察者Observer观察者是接收主题通知的对象。观察者需要实现一个更新方法当收到主题的通知时调用该方法进行更新操作。具体主题Concrete Subject具体主题是主题的具体实现类。它维护着观察者列表并在状态发生改变时通知观察者。具体观察者Concrete Observer具体观察者是观察者的具体实现类。它实现了更新方法定义了在收到主题通知时需要执行的具体操作。
观察者模式通过将主题和观察者解耦实现了对象之间的松耦合。当主题的状态发生改变时所有依赖于它的观察者都会收到通知并进行相应的更新。
优缺点
优点 1、观察者和被观察者是抽象耦合的。 2、建立一套触发机制。
缺点 1、如果一个被观察者对象有很多的直接和间接的观察者的话将所有的观察者都通知到会花费很多时间。 2、如果在观察者和观察目标之间有循环依赖的话观察目标会触发它们之间进行循环调用可能导致系统崩溃。 3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的而仅仅只是知道观察目标发生了变化。
代码实现
观察者模式使用三个类 Subject、Observer 和 Client。Subject 对象带有绑定观察者到 Client 对象和从 Client 对象解绑观察者的方法。 python代码
class Subject:def __init__(self):self._observers []self._state Nonedef get_state(self):return self._statedef set_state(self, state):self._state stateself._notify_all_observers()def attach(self, observer):self._observers.append(observer)def _notify_all_observers(self):for observer in self._observers:observer.update()class Observer:def __init__(self, subject):self._subject subjectdef update(self):passclass BinaryObserver(Observer):def __init__(self, subject):super().__init__(subject)self._subject.attach(self)def update(self):state self._subject.get_state()print(Binary String:, bin(state)[2:])class OctalObserver(Observer):def __init__(self, subject):super().__init__(subject)self._subject.attach(self)def update(self):state self._subject.get_state()print(Octal String:, oct(state)[2:])class HexaObserver(Observer):def __init__(self, subject):super().__init__(subject)self._subject.attach(self)def update(self):state self._subject.get_state()print(Hex String:, hex(state)[2:].upper())if __name__ __main__:subject Subject()HexaObserver(subject)OctalObserver(subject)BinaryObserver(subject)print(First state change: 15)subject.set_state(15)print(Second state change: 10)subject.set_state(10)执行结果
go代码
package mainimport (fmtstrconv
)type Observer interface {Update()
}type BinaryObserver struct {subject *Subject
}func NewBinaryObserver(subject *Subject) {bin : new(BinaryObserver)bin.subject subjectbin.subject.Attach(bin)
}func (bin *BinaryObserver) Update() {fmt.Println(strconv.FormatInt(int64(bin.subject.stat), 2))
}type Subject struct {stat intobservers []Observer
}func NewSubject() *Subject {return Subject{observers: make([]Observer, 0),}
}func (sub *Subject) Attach(observer Observer) {sub.observers append(sub.observers, observer)
}func (sub *Subject) Notify() {for _, obs : range sub.observers {obs.Update()}
}func (sub *Subject) GetStat() int {return sub.stat
}func (sub *Subject) SetStat(stat int) {sub.stat statsub.Notify()
}func main() {sub : NewSubject()// 注册到sub中NewBinaryObserver(sub)sub.SetStat(10)
}解释器模式
解释器模式Interpreter Pattern提供了评估语言的语法或表达式的方式它属于行为型模式。这种模式实现了一个表达式接口该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。
使用场景
意图给定一个语言定义它的文法表示并定义一个解释器这个解释器使用该标识来解释语言中的句子。
主要解决对于一些固定文法构建一个解释句子的解释器。
何时使用如果一种特定类型的问题发生的频率足够高那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器该解释器通过解释这些句子来解决该问题。
如何解决构建语法树定义终结符与非终结符。
应用实例编译器、运算表达式计算。
使用场景 1、可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。 2、一些重复出现的问题可以用一种简单的语言来进行表达。 3、一个简单语法需要解释的场景。
优缺点
优点 1、可扩展性比较好灵活。 2、增加了新的解释表达式的方式。 3、易于实现简单文法。
缺点 1、可利用场景比较少。 2、对于复杂的文法比较难维护。 3、解释器模式会引起类膨胀。 4、解释器模式采用递归调用方法。
代码实现
创建一个接口 Expression 和实现了 Expression 接口的实体类。定义作为上下文中主要解释器的 TerminalExpression 类。其他的类 OrExpression、AndExpression 用于创建组合式表达式。 python代码
# Interpreter Pattern with Python Code
from abc import abstractmethod,ABCMeta
#创建一个表达式接口
class Expression(metaclassABCMeta):abstractmethoddef interpret(self, inContext):pass
# 创建实现Expression接口的实体类
class TerminalExpression(Expression):_data def __init__(self,inData):self._data inDatadef interpret(self,inContext):if inContext.find(self._data) 0:return Truereturn False
class OrExpression(Expression):_expr1 None_expr2 Nonedef __init__(self,inExpr1,inExpr2):self._expr1 inExpr1self._expr2 inExpr2def interpret(self, inContext):return self._expr1.interpret(inContext) or self._expr2.interpret(inContext)
class AndExpression(Expression):_expr1 None_expr2 Nonedef __init__(self,inExpr1,inExpr2):self._expr1 inExpr1self._expr2 inExpr2def interpret(self, inContext):return self._expr1.interpret(inContext) and self._expr2.interpret(inContext)
# 调用输出
if __name__ __main__:# 规则Robert和John是男性 def getMaleExpression(): robert TerminalExpression(Robert) john TerminalExpression(John) return OrExpression(robert,john) # 规则Julie是一个已婚的女性 def getMarriedWomanExpression(): julie TerminalExpression(Julie) married TerminalExpression(Married) return AndExpression(julie,married) isMale getMaleExpression() isMarriedWoman getMarriedWomanExpression() print(John is male? str(isMale.interpret(John))) print(Julie is a married women? str(isMarriedWoman.interpret(Married Julie)))go代码
package mainimport (fmtstrings
)type Expression interface {interpret(string) bool
}type TerminalExpression struct {data string
}func (t *TerminalExpression) interpret(ctx string) bool {return strings.Contains(ctx, t.data)
}type OrExpression struct {expr1, expr2 Expression
}func (or *OrExpression) interpret(ctx string) bool {return or.expr1.interpret(ctx) || or.expr2.interpret(ctx)
}type AndExpression struct {expr1, expr2 Expression
}func (and *AndExpression) interpret(ctx string) bool {return and.expr1.interpret(ctx) and.expr2.interpret(ctx)
}func main() {// 为解释器定义语法将John,Robert解释为trueisMale : new(OrExpression)isMale.expr1 TerminalExpression{data: John}isMale.expr2 TerminalExpression{data: Robert}//isMarriedWoman : new(AndExpression)isMarriedWoman.expr1 TerminalExpression{data: Julie}isMarriedWoman.expr2 TerminalExpression{data: Married}fmt.Println(John is male?, isMale.interpret(John))}备忘录模式
备忘录模式Memento Pattern保存一个对象的某个状态以便在适当的时候恢复对象。备忘录模式属于行为型模式。
使用场景
意图在不破坏封装性的前提下捕获一个对象的内部状态并在该对象之外保存这个状态。
主要解决所谓备忘录模式就是在不破坏封装的前提下捕获一个对象的内部状态并在该对象之外保存这个状态这样可以在以后将对象恢复到原先保存的状态。
何时使用很多时候我们总是需要记录一个对象的内部状态这样做的目的就是为了允许用户取消不确定或者错误的操作能够恢复到他原先的状态使得他有后悔药可吃。
应用实例 1、后悔药。 2、打游戏时的存档。 3、Windows 里的 ctrl z。 4、IE 中的后退。 5、数据库的事务管理。
使用场景 1、需要保存/恢复数据的相关状态场景。 2、提供一个可回滚的操作。
注意事项 1、为了符合迪米特原则还要增加一个管理备忘录的类。 2、为了节约内存可使用原型模式备忘录模式
优缺点
优点 1、给用户提供了一种可以恢复状态的机制可以使用户能够比较方便地回到某个历史的状态。 2、实现了信息的封装使得用户不需要关心状态的保存细节。
缺点消耗资源。如果类的成员变量过多势必会占用比较大的资源而且每一次保存都会消耗一定的内存。
代码实现
备忘录模式使用三个类 Memento、Originator 和 CareTaker。Memento 包含了要被恢复的对象的状态。Originator 创建并在 Memento 对象中存储状态。Caretaker 对象负责从 Memento 中恢复对象的状态。
python代码
# Memento Pattern with Python Code
from abc import abstractmethod,ABCMeta# 创建Memento类
class Memento():_state def __init__(self,strState):self._state strStatedef getState(self):return self._state# 创建Originator类
class Originator():_state def setState(self,strState):self._state strStatedef getState(self):return self._statedef saveStateToMemento(self):return Memento(self._state)def getStateFromMemento(self,inMemento):self._state inMemento.getState()# 创建CareTaker类
class CareTaker():_mementoList []def add(self,inMemento):self._mementoList.append(inMemento)def get(self,inIndex):return self._mementoList[inIndex]# 调用输出
if __name__ __main__:originator Originator()careTaker CareTaker()originator.setState(State #1)originator.setState(State #2)careTaker.add(originator.saveStateToMemento())originator.setState(State #3)careTaker.add(originator.saveStateToMemento())originator.setState(State #4)print(Current State: originator.getState())originator.getStateFromMemento(careTaker.get(0))print(First saved State: originator.getState())originator.getStateFromMemento(careTaker.get(1))print(First saved State: originator.getState())go代码
package mainimport fmt// 存档
type Memento struct {stat int
}func NewMemento(stat int) *Memento {return Memento{stat: stat}
}// 主线
type Originator struct {stat int
}func (o *Originator) Set(stat int) {o.stat stat
}func (o *Originator) saveStateToMemento() *Memento {return Memento{stat: o.stat}
}func (o *Originator) getStateFromMemento(m *Memento) {o.stat m.stat
}type CareTaker struct {core []*Memento
}func NewCareTaker() *CareTaker {return CareTaker{core: make([]*Memento, 0)}
}func (taker *CareTaker) Add(m *Memento) {taker.core append(taker.core, m)
}func (taker *CareTaker) Get(index int) *Memento {if index len(taker.core) {return nil}return taker.core[index]
}func main() {origin : Originator{stat: 0}taker : NewCareTaker()origin.Set(1)taker.Add(origin.saveStateToMemento())fmt.Println(current, origin.stat)origin.Set(3)fmt.Println(current, origin.stat)origin.getStateFromMemento(taker.Get(0))fmt.Println(current, origin.stat)
}执行结果 状态模式
在状态模式State Pattern中类的行为是基于它的状态改变的。这种类型的设计模式属于行为型模式。
在状态模式中我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。
使用场景
意图允许对象在内部状态发生改变时改变它的行为对象看起来好像修改了它的类。
主要解决对象的行为依赖于它的状态属性并且可以根据它的状态改变而改变它的相关行为。
何时使用代码中包含大量与对象状态有关的条件语句。
使用场景 1、行为随状态改变而改变的场景。 2、条件、分支语句的代替者。
注意事项在行为受状态约束的时候使用状态模式而且状态不超过 5 个。
优缺点
优点 1、封装了转换规则。 2、枚举可能的状态在枚举状态之前需要确定状态种类。 3、将所有与某个状态有关的行为放到一个类中并且可以方便地增加新的状态只需要改变对象状态即可改变对象的行为。 4、允许状态转换逻辑与状态对象合成一体而不是某一个巨大的条件语句块。 5、可以让多个环境对象共享一个状态对象从而减少系统中对象的个数。
缺点 1、状态模式的使用必然会增加系统类和对象的个数。 2、状态模式的结构与实现都较为复杂如果使用不当将导致程序结构和代码的混乱。 3、状态模式对开闭原则的支持并不太好对于可以切换状态的状态模式增加新的状态类需要修改那些负责状态转换的源代码否则无法切换到新增状态而且修改某个状态类的行为也需修改对应类的源代码。
代码实现
创建一个 State 接口和实现了 State 接口的实体状态类。Context 是一个带有某个状态的类。
python代码
class State:def __str__(self):return self.__class__.__name__def do_action(self, context: Context):raise NotImplementedError()class StopState(State):def do_action(self, context: Context):print(Player is in stop state)context.state selfclass StartState(State):def do_action(self, context: Context):print(Player is in start state)context.state selfclass Context:def __init__(self):self._state Nonepropertydef state(self):return self._statestate.setterdef state(self, state: State):self._state stateif __name__ __main__:ctx Context()start StartState()start.do_action(ctx)print(ctx.state)go代码
package mainimport fmttype State interface {doAction(*Context)
}type StartState struct {
}func (start *StartState) doAction(ctx *Context) {fmt.Println(Player is in start state)ctx.SetState(start)
}func (start *StartState) String() string {return startState
}type Context struct {state State
}func (ctx *Context) SetState(state State) {ctx.state state
}func (ctx *Context) GetState() State {return ctx.state
}func main() {ctx : Context{state: nil}start : StartState{}start.doAction(ctx)fmt.Println(ctx.GetState())
}
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/85148.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!