设计模式

学习设计模式的途中,看各种网上的资料和博客,也积累了一点东西,拿出来分享一下~


创建型设计模式

  1. Singleton
    很好的博客,在多线程方面讲的非常透彻。
  2. Builder

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    abstract 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 来协助维持总体的组装。
  3. Prototype

    1
    2
    // 原型模式实际上就在于实现 Cloneable 接口。能够让对象进行深拷贝,而不用一个一个全部重新构造。
    // 不过,要注意防止循环引用的现象发生。
  4. Abstract Factory
    抽象工厂

    1
    2
    抽象工厂是 “工厂的工厂”。这类抽象的工厂通过传入的参数来返回工厂对象。因而这类抽象工厂是 “工厂的产生器”。
    工厂内部可以产生不同类型的对象。这些对象在被继承的时候,可以把没有关系但是却又不得不继承的方法设置为空方法。
  5. Factory
    工厂

    1
    工厂类顾名思义,其实就是创建一个工厂来通过传入的参数来返回创建好的产品对象。比较常用。这个工厂只生产一个借口下的多个子类。而 Abstract Factory 可以生产多个不同接口类下的子方法。

行为型设计模式

  1. Strategy

    1
    // 使用[继承体系],继承同一个接口,只不过每种子类有不同的实现。然后通过把这继承体系的对象当做参数,可以选择不同的算法。其实就像 C++ STL 中的非标准的 std::add, std::sub, std::mul 等等。每个重载 operator() 都是不同的算法。通过把这些 adaptor 传递给接受他们的函数,就可以相当于使用这些算法了。
  2. State

    1
    2
    3
    4
    5
    6
    7
    8
    // State 首先是一个接口而引发的继承体系。State 会有多种。
    // 其实吧,如果我有一个 boy 对象,他的状态发生了改变的话,明明是 boy 自身的变动,却由程序员[我]来手动调用 boy.setState(new BadState()) 来帮他上状态,确实很诡异...... 所以这里,为了符合语义也一定要反过来。也就是,状态 State 占主导,接收参数来改变 boy。即:
    class BadState extends State{
    void doAction(Boy boy) {
    boy.setState(this); // 还是这样顺眼一些......
    }
    }
  3. Chain of Responsibility (责任链模式)
    真是玄奥……并不明白它会被应用在哪里……

    1
    2
    // 其实本质上是一个链表结构。然后每个节点被封装了一个 level,节点内部有一个处理责任链的方法,这个方法是递归实现的,接收另一个 need_level 参数,会对每个接下来的节点进行处理。整个链表上 this.level > need_level 的节点都将会被忽略掉。也就是传入参数 need_level 是紧急的程度。紧急程度越高,就越会激活大部分的责任链了。
    这个还要好好看一看复习下咯。
  4. Interpreter
    解释器模式……parse 成 AST……

    1
    额呵呵呵......写一个解释器 ????? 有待考察......
  5. Command
    命令模式:把函数的执行包装成一个对象!

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    // 这样把 “函数的执行动作” 变得 “实体化” 了,就可以实现数据库的事务等等了。这样,任务的执行都是被记录的,而且可以批量进行执行。
    // 统括思想:创建一个具有继承体系的 “命令对象”,本质是一个接口继承而来。这个通用的统一接口下,类似策略模式以及配接器模式,会对要被执行的函数进行一个封装,即把不同的策略封装在不同的类产生的对象中,但是接口统一地去进行【持久化】。然后,如果要执行一套连续的动作的话,只需要把保存在 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;
    }
    @Override
    void executeAction(){
    a.buy(); // 这样的话,一个 “过程” 就被封装成为了一个类对象了!!再包装一层永远是解决问题的策略。
    }
    }
    class SellOrder implements Order{
    private Action a;
    public SellOrder(Action a){
    this.a = a;
    }
    @Override
    void executeAction(){
    a.sell();
    }
    }
    class EatOrder implements Order{
    private Action a;
    public EatOrder(Action a){
    this.a = a;
    }
    @Override
    void executeAction(){
    a.eat();
    }
    }
  6. Observer

    1
    可以对应 Java GUI 中的 “通知” 的 action。适用于一个组件发生了改变,然后可以对多个其他的组件进行主动通知的设计模式。当然,需要这被通知的多个组件具有相同的接口才行!!比如,都实现了统一的 update() 方法!要不,如果方法签名都不统一,上哪去调用去......
  7. Memento (备忘录模式)
    Memento

    1
    2
    可以适用于游戏存档的场合。
    里边有 3 个类:Memento(包装一个 state 的结构)、Originator(产生 Memento 的工厂)、CareTaker(一个 Memento 的 list)。记住这三点,就非常简单。而 CareTaker 是通过操纵 Originator 来间接产生 Memento 对象存档的。
  8. Iterator

    1
    STL 中全部如此。略了。
  9. Template Method

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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();
    }
    }
  10. Visitor
    Parser 的 Visitor 设计模式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    // 轮大讲的非常好~
    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{ // 已经进入了 AddExpr
    result = 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 的吧。
    }
  11. Mediator (中介者模式)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // 中介者模式顾名思义,其实就是提供了一个中介,来降低双方通信的复杂度。在双方要进行通信的时候,都会调用中介者来显示信息。
    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!");
    }

结构型设计模式

  1. Decorator
    Java IO 中经常用到。BufferedInputStream 等其实就是对底层 IO 的封装。
    在继承体系当中,子类含有一个接口类的对象,虽然这个子类也是接口类的对象。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    interface 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 进行 [装饰扩展]。
    ... // 扩展
    }
    }
  2. Proxy

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    // 套路和上边的 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 差不多。
    }
    }
  3. Composite

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    类比于文件系统。
    所有的一切,包括容器类全都继承自同一个接口。适用于树形结构。
    abstract class File {
    ...
    }
    class Folder extends File {
    File[] files;
    ...
    };
    class TXT extends File{
    ...
    }
    class PNG extends File{
    ...
    }
  4. Bridge
    Bridge

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 调用方式:(先写出调用方式可能更好一些。)
    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() 来进行转发。
  5. Adapter

    1
    配接器模式,在 STL 中用得非常多。比如 vector -> stack,还是 std::bind2nd 等等。
  6. Flyweight (享元模式)

    1
    2
    其实就是使用 HashMap 来保存和提取公有对象而已。和 STL 的 map 的 operator[] 原理简直一模一样。
    当然,非常有可能和 [工厂模式] 以及 [单例模式] 联协使用。可以用一个 Singleton 的 Factory 内部包装一个 HashMap 的意思~
  7. Facade (外观模式)
    Facade

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    // 用 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();
    }
    }