学习设计模式的途中,看各种网上的资料和博客,也积累了一点东西,拿出来分享一下~
创建型设计模式
- Singleton
很好的博客,在多线程方面讲的非常透彻。 Builder
123456789101112131415161718192021222324252627282930abstract class Builder {protected String name;protected int level;public void setName(String name) {this.name = name;}public void setLevel(int level) {this.level = level;}public abstract Instance build();}abstract class Director {protected Builder builder;protected String name;public Director(Builder builder) {this.builder = builder;}public Instance construct(Args...args) {builder.setName(args[0]);builder.setLevel(args[1]);......return builder.build();}}// 和工厂方法的不同在于:builder 还有一个 director 来使用内部的 builder 来协助维持总体的组装。Prototype
12// 原型模式实际上就在于实现 Cloneable 接口。能够让对象进行深拷贝,而不用一个一个全部重新构造。// 不过,要注意防止循环引用的现象发生。Abstract Factory
抽象工厂12抽象工厂是 “工厂的工厂”。这类抽象的工厂通过传入的参数来返回工厂对象。因而这类抽象工厂是 “工厂的产生器”。工厂内部可以产生不同类型的对象。这些对象在被继承的时候,可以把没有关系但是却又不得不继承的方法设置为空方法。Factory
工厂1工厂类顾名思义,其实就是创建一个工厂来通过传入的参数来返回创建好的产品对象。比较常用。这个工厂只生产一个借口下的多个子类。而 Abstract Factory 可以生产多个不同接口类下的子方法。
行为型设计模式
Strategy
1// 使用[继承体系],继承同一个接口,只不过每种子类有不同的实现。然后通过把这继承体系的对象当做参数,可以选择不同的算法。其实就像 C++ STL 中的非标准的 std::add, std::sub, std::mul 等等。每个重载 operator() 都是不同的算法。通过把这些 adaptor 传递给接受他们的函数,就可以相当于使用这些算法了。State
12345678// State 首先是一个接口而引发的继承体系。State 会有多种。// 其实吧,如果我有一个 boy 对象,他的状态发生了改变的话,明明是 boy 自身的变动,却由程序员[我]来手动调用 boy.setState(new BadState()) 来帮他上状态,确实很诡异...... 所以这里,为了符合语义也一定要反过来。也就是,状态 State 占主导,接收参数来改变 boy。即:class BadState extends State{void doAction(Boy boy) {boy.setState(this); // 还是这样顺眼一些......}}Chain of Responsibility (责任链模式)
真是玄奥……并不明白它会被应用在哪里……12// 其实本质上是一个链表结构。然后每个节点被封装了一个 level,节点内部有一个处理责任链的方法,这个方法是递归实现的,接收另一个 need_level 参数,会对每个接下来的节点进行处理。整个链表上 this.level > need_level 的节点都将会被忽略掉。也就是传入参数 need_level 是紧急的程度。紧急程度越高,就越会激活大部分的责任链了。这个还要好好看一看复习下咯。Interpreter
解释器模式……parse 成 AST……1额呵呵呵......写一个解释器 ????? 有待考察......Command
命令模式:把函数的执行包装成一个对象!1234567891011121314151617181920212223242526272829303132333435363738394041424344454647// 这样把 “函数的执行动作” 变得 “实体化” 了,就可以实现数据库的事务等等了。这样,任务的执行都是被记录的,而且可以批量进行执行。// 统括思想:创建一个具有继承体系的 “命令对象”,本质是一个接口继承而来。这个通用的统一接口下,类似策略模式以及配接器模式,会对要被执行的函数进行一个封装,即把不同的策略封装在不同的类产生的对象中,但是接口统一地去进行【持久化】。然后,如果要执行一套连续的动作的话,只需要把保存在 list 中的这些对象统一取出来,再进行逐一遍历,然后调用统一的接口函数即可。【在学习设计模式的时候,如果想要学得深入的话,就要先想想如果是自己的话,会怎么做。然后再学习前人们的成果,最后还一定要多加重复才行。】// 比如我有一个类,它有多种动作:class Action{void buy();void sell();void eat();}// 为了把这些瞬时的 “动作” 持久化成为 “永久的”,我们创建一套 Order (命令)类组合:interface Order{void executeAction();}class BuyOrder implements Order{private Action a;public BuyOrder(Action a){this.a = a;}void executeAction(){a.buy(); // 这样的话,一个 “过程” 就被封装成为了一个类对象了!!再包装一层永远是解决问题的策略。}}class SellOrder implements Order{private Action a;public SellOrder(Action a){this.a = a;}void executeAction(){a.sell();}}class EatOrder implements Order{private Action a;public EatOrder(Action a){this.a = a;}void executeAction(){a.eat();}}Observer
1可以对应 Java GUI 中的 “通知” 的 action。适用于一个组件发生了改变,然后可以对多个其他的组件进行主动通知的设计模式。当然,需要这被通知的多个组件具有相同的接口才行!!比如,都实现了统一的 update() 方法!要不,如果方法签名都不统一,上哪去调用去......Memento (备忘录模式)
Memento12可以适用于游戏存档的场合。里边有 3 个类:Memento(包装一个 state 的结构)、Originator(产生 Memento 的工厂)、CareTaker(一个 Memento 的 list)。记住这三点,就非常简单。而 CareTaker 是通过操纵 Originator 来间接产生 Memento 对象存档的。Iterator
1STL 中全部如此。略了。Template Method
12345678910111213在 abstract 基类中定义一个 public final 方法作为指定好的模板算法骨架,这个骨架会按某种顺序调用此类的其他方法。而继承此 abstract 类的子类,只需要重写这些 “其他方法” 就可以了。abstract class Template{abstract void first();abstract void second();abstract void third();public final void executeInOrder(){first(); // 执行模板已经定义好,**必须**按照顺序执行。子类只需要重写 first(), second(), third() 即可。second();third();}}Visitor
Parser 的 Visitor 设计模式12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364// 轮大讲的非常好~class IVisitor{public:virtual ~IVisitor() = default;virtual void visit(ValueExpr *node) = 0;virtual void visit(AddExpr *node) = 0;virtual void visit(MulExpr *node) = 0;}class Expr{public:virtual ~Expr() = 0;virtual void accept(IVisitor *visitor) = 0;}class ValueExpr : public Expr{public: // 注意:数据全是 public 的。如果要设置 private,请设置 IVisitor 为友元。double value;virtual void accept(IVisitor *visitor) override{visitor->visit(this);}}class AddExpr : public Expr{public:shared_ptr<Expr> x;shared_ptr<Expr> y;virtual void accept(IVisitor *visitor) override{visitor->visit(this);}}class MulExpr : public Expr{public:shared_ptr<Expr> x;shared_ptr<Expr> y;virtual void accept(IVisitor *visitor) override{ // 接受参观者visitor->visit(this); // 参观者进行访问动作,即改写 visitor 自身的 result}}class EvalVisitor : public IVisitor{public:double result;double call(Expr *node) { // visitor 呼叫(通知,也就是按门铃)被参观者叫他接受 (accpet) 自己 (给自己开门)node->accept(this);return result;}void visit(ValueExpr *node) override{result = node->value;}void visit(AddExpr *node) override{ // 已经进入了 AddExprresult = call(node->x.get()) + call(node->y.get()); // 还想访问 AddExpr 内部的两个元素。}void visit(MulExpr *node) override{result = call(node->x.get()) + call(node->y.get());}}double Eval(shared_ptr<Expr> node) {EvalVisitor visitor;node->accept(&visitor); // accept 才是真的入口???return visitor.result; // 其实这两句写成一句 call 也行。不过一般 call 都是被设置成为 private 的吧。}Mediator (中介者模式)
1234567891011121314151617// 中介者模式顾名思义,其实就是提供了一个中介,来降低双方通信的复杂度。在双方要进行通信的时候,都会调用中介者来显示信息。class Chatroom {public static showMessage(String msg) { sysout...; }}class User {public sendMsg(String msg) {Chatroom.showMessage(msg); // 这样。直接调用中介者 Chatroom 显示在 Chatroom 的屏幕上。}}public static void main (String[] args) {User jack = new User();User tom = new User();jack.sendMsg("hello!");tom.sendMsg("hi!");}
结构型设计模式
Decorator
Java IO 中经常用到。BufferedInputStream 等其实就是对底层 IO 的封装。
在继承体系当中,子类含有一个接口类的对象,虽然这个子类也是接口类的对象。1234567891011121314151617181920interface Worker{void work();}class Son implements Worker{void work() {...}}class Mother implements Worker{private Worker worker; // 内部有一个接口对象,可能是 Son。public Mother(Worker worker){this.worker = worker;}void work() {worker.work(); // 基于内部的 worker 写好的 work 进行 [装饰扩展]。... // 扩展}}Proxy
123456789101112131415161718192021222324// 套路和上边的 Decorator 很像,不过它的目的并不是用于加强,而是用于控制。interface Worker{void work();}class Son implements Worker{void work() {...}}class WorkerProxy implements Worker{ // 也要继承 Worker。因为要保持接口一致。private Son son = null; // 对象是固定的 Son。因为是此类是针对 特定类 特化的 代理类。public WorkerProxy() {// nothing}void work() {if (son == null) {son = new Son(); // 没有的话就创建。相当于 Lazy Singleton。}son.work(); // 不增强。而是直接调用 son。这就是个代理,要尊重原作,并不像 Decorator 一样加以修改。// 第二次调用此 work 方法,son 就不必 new 了。这点和 Decorator 差不多。}}Composite
123456789101112131415类比于文件系统。所有的一切,包括容器类全都继承自同一个接口。适用于树形结构。abstract class File {...}class Folder extends File {File[] files;...};class TXT extends File{...}class PNG extends File{...}Bridge
Bridge1234567891011// 调用方式:(先写出调用方式可能更好一些。)public static void main(String[] args) {Shape redCircle = new Circle(100,100, 10, new RedCircle());Shape greenCircle = new Circle(100,100, 10, new GreenCircle());redCircle.draw();greenCircle.draw();}// 其中 Circle 是直接继承于 Shape 的。RedCircle 和 GreenCircle 是直接实现于 DrawAPI 的。// Circle.draw() 的时候,内部会直接调用成员变量 RedCircle.drawAPI() 和 GreenCircle.drawAPI() 来进行转发。Adapter
1配接器模式,在 STL 中用得非常多。比如 vector -> stack,还是 std::bind2nd 等等。Flyweight (享元模式)
12其实就是使用 HashMap 来保存和提取公有对象而已。和 STL 的 map 的 operator[] 原理简直一模一样。当然,非常有可能和 [工厂模式] 以及 [单例模式] 联协使用。可以用一个 Singleton 的 Factory 内部包装一个 HashMap 的意思~Facade (外观模式)
Facade12345678910111213141516171819202122232425// 用 Shape 这个接口产生 Circle,Rectangle 以及 Square 三个类型,共有 draw() 接口。// 为了隐藏逐个调用的复杂性,使用一个更高层次的类 ShapeMaker 来封装,client 不必知道系统内部的复杂性,整个系统只需要提供一个封装好的 “接待员” 即可。// 这段代码比较能够反映核心,因而拷贝过来。public class ShapeMaker {private Shape circle;private Shape rectangle;private Shape square;public ShapeMaker() {circle = new Circle();rectangle = new Rectangle();square = new Square();}public void drawCircle(){circle.draw();}public void drawRectangle(){rectangle.draw();}public void drawSquare(){square.draw();}}