设计模式 很早前看过,那时候囫囵吞枣,从来没有系统的总结过,因为对于面试而言问的问题总是答的不精确。
这次又借助《图解设计模式》复习了一遍,自己写了一篇总结。
23种设计模式 看起来很多其实大多数在开发中都见到过。甚至有的设计模式对于一个初学者而言即使不知道设计模式也会应用。
我想从 这方面入手 将设计模式进行分类,这样 可以让一些初次了解设计模式的人知道哪些要重点记忆 而哪些一点即通。
一、简单到新手会在开发中自己摸索出来的设计模式
1、Facade模式 [外观模式]
定义:为多个客户端提供一个统一的接口用于访问子系统中的一群接口。
我觉得 Facade 模式是最简单的。并认为一个程序小白在自学编程的道路上自然而然的就会用上 Facade 模式。
Facade 就是让复杂的东西变简单。
以造车为例子。
public class Car{public Engine engine;public List<Wheel> wheels;
}class Engine{public String name; //引擎名字
}class Wheel{public String name; //车轮名字
}
如果我们正常放在Main方法里
public static void main(String[] args){Car myCar = new Car();Engine engine = new Engine("myEngine");myCar.setEngine(engine);for(int i = 0; i < 4; i++){Wheel wheel = new Wheel("myWheel_" + i);myCar.getWheelList().add(wheel);}
}
如果以上代码这么简单还好如果处在一个巨大的项目中,就让会代码变得复杂这时候我们创建出一个类来同一管理即可
public class CarMaker{private CarMaker(){}public static Car makeNewCar(String engineName, String wheelName){Car myCar = new Car();Engine engine = new Engine(engineName);myCar.setEngine(engine);for(int i = 0; i < 4; i++){Wheel wheel = new Wheel(wheelName + "_" + i);myCar.getWheelList().add(wheel);}return myCar;}
}//这时候我们就获得了简洁的代码
public static void main(String[] args){Car carm = CarMaker.makeNewCar("myNewEngine", "myNewWheel");
}
正如我所说这种模式是一个程序员即使不知道设计模式也能自己总结出来的。
2、Adapter 模式 [适配器模式]
适配器模式的核心思想就是 "复用"。 假设已经有一个古早的类实现了两个功能。
而在代码重构过程中,不想改动这个类,但是希望它拥有整洁的代码。
// 旧代码
public class Banner{public void showWithParen(){/*省略*/}public void showWithAster(){/*省略*/}
}
新代码我们实现了新的 show 模式,而且 方法方式取名也做了修改,重写的话就会动到之前的其他引用代码。这肯定会引发头疼的问题。
我们可以让 让新的类 继承 这个旧类,然后并将新方法写入到这里面
class interface Print{public abstract void printWeak();public abstract void printStrong();public abstract void underLine();
}public class PrintBanner extends Banner implements Print{public void printWeak(){showWithParen();}public void printStrong(){showWithAster();}public void printUnderLine(){/*省略*/}
}
这样处理之后,旧框架下使用 Print类的其他代码不会出错。而且新代码也做了重构,并在不入侵 Print 的情况下,做了功能拓展。
上述代码实现方式在 该模式下叫做 类适配器模式(使用继承)
还有一种是对象适配器模式
public calss PrintBanner implements Print{//将输出委托给Banner;private Banner banner;public void printWeak(){banner.showWithParen();}public void printStrong(){banner.showWithAster();}public void printUnderLine(){/*省略*/}
3、Strategy 模式 [策略模式]
假设设计一个 AI 怪物。有 简单 中等 疯狂 三种行动策略。
那么我们就要对三种策略进行定义。 怪物使用的策略我们同一用接口 Strategy 表示。
每一步都要进行判断
public interface Strategy{public abstract Step abstract nextStep();
}public class SimpleStrategy implements Strategy{public abstract Step nextStep(){return new Step("flee");}
}public class NormalStrategy implements Strategy{public abstract Step nextStep(){return new Step("waitting");}
}public class MadStrategy implements Strategy{public abstract Step nextStep(){return new Step("attack");}
}
public class Enemy{private Strategy strategy;public Enemy(Strategy strategy){this.strategy = strategy}public void nextStep(){Step step = strategy.nextStep();}
}
这时候 就是用了不同的策略。
4、State 模式 [状态模式]
说实在的,我感觉 State 模式和 Strategy 模式的思想一摸一样。例如Strategy模式下的代码,只需稍加修改就能变成状态模式
public interface State{public abstract Step abstract nextStep();
}public class FleeState implements State{private static FleeState singleton = new FleeState();public static State getInstance(){return singleton;}public abstract Step nextStep(){return new Step("flee");}
}public class WattingState implements State{private static WattingState singleton = new WattingState ();public static State getInstance(){return singleton;}public abstract Step nextStep(){return new Step("waitting");}
}public class AttackState implements State{private static AttackState singleton = new AttackState ();public static State getInstance(){return singleton;}public abstract Step nextStep(){return new Step("attack");}
}
public class Enemy{private State state;public Enemy(State state){this.state = state;}public void changeState(State state){this.state = state;}public void nextStep(){Step step = state.nextStep();}
}
唯一不同的是 策略是自己类内定的。【当然也可以写额外方法修改自身策略】
而状态是外部环境变换改变的。
不过在 State 在 《图解设计模式》 这本书上给的是单例模式。我想 Strategy 也可以是单例吧。
这里本人只是认为思想大同小异,如果哪里有误还请读者指出。
5、Memento 模式 [备忘录模式]
这个模式一句话 在游戏开发里 就是 保存 然后 加载。
继续上一节关于怪物状态的代码。
public class Enemy{private int id;private State state;private int hp;//血量public Enemy(State state){this.state = state;}public void changeState(State state){this.state = state;}public void nextStep(){Step step = state.nextStep();}public Memento craeteMemento(){Memento memento = new Memento();memento.setId(this.id);memento.setState(this.state);memento.setHp(this.hp);return memento;}public void restoreMemento(Memento memento){/* 省略 */}
}public Memento{private int id;private State state;private int hp;//血量
}
em.. 其实就是这样。其实就是希望一个类来自由的访问对象的数据。并进行存储和恢复。
6、Flyweight 模式 [享元模式]
这个模式我理解就是缓存,假设一个类可以获得某个对象,这个对象生成需要时间,而且并不是一次性使用,我们可以在用户获取后对该对象进行缓存,下次再使用就可以直接给出。
现在假设有个类可以破解 一个密码算法吧,同一个密码可能会被多次查询【我是说假设】。
public class Decode{public static String handleDecode(String strCode){String decodeStr = /* 破解代码 */ strCode;return decodeStr;}
}public class DecodeFactory{private HashMap<String,String> deCodeMap = new HashMap();Decode decode ;//防止多线程问题public sychronized static decode(String codeStr){String decodeStr = deCodeMap.get(codeStr);if(decodeStr == null){decodeStr = decode.handleDecode(codeStr);deCodeMap.put(codeStr,decodeStr);}return decodeStr;}
}
二、从代码中稍加注意便能掌握的模式
7、Iterator 模式 [迭代子模式]
这个模式无需多言了,只要开始写 java 代码
实现方式就是定义一个聚合类,然后通过这个聚合类来获取 iterator 对象。然后遍历
public interface Aggregate{public abstract Iterator iterator();
}public interface Iterator{public abstract boolean hasNext();public abstract Object next();
}
比如 书类 定义一个 书籍类,书籍类 实现 Aggregate 接口。
然后顶一个 书籍Iterator类 实现 Iterator 接口。
public class BookShelfIterator implements Iterator{/* 省略 */
}public class BookShelf implements Aggregate{private Book[] books;public Iterator iterator(){return new BookShelfIterator(this);}
}
8、Template Method 模式 [模板方法模式]
就是说有个 抽象类 给 规定了一些行为,然后不同的类 继承抽象类。根据自己的方式去实现这些方法。
public abstract class AbstractDisplay{public abstract void open();public abstract void print();public abstract void close();public final void display(){open();print();close();}
}public class CharDisplay extends AbstractDisplay{public void open(){System.outl.print("<<");}public void print(){System.outl.print("我是CharDisplay");}public void close(){System.outl.print(">>");}
}public class StringDisplay extends AbstractDisplay{public void open(){System.outl.print("[[");}public void print(){System.outl.print("我是StringDisplay");}public void close(){System.outl.print("]]");}
}
最终行为 display 方法是 抽象类定的。但是其他实现是子类做的。
9、Builder 模式 [建造者模式]
其实建造者模式和 模板模式有点像。
都是一个抽象类被不同行为的类继承。
这里面的区别是
模板方法类是 父类直到行为【比如上节代码 AbstractDisplay 的 display 定义好了行为】
而建造者类是 有其他类指导行为。【《图解设计模式》中使用 Director 表示】
这里我将代码略微修改可以看出区别
public abstract class Builder{public abstract void open();public abstract void print();public abstract void close();//删去父类指导部分// public final void display(){// open();// print();// close();// }
}public class CharBuilder extends Builder{public void open(){System.outl.print("<<");}public void print(){System.outl.print("我是CharDisplay");}public void close(){System.outl.print(">>");}
}public class StringBuilder extends Builder{public void open(){System.outl.print("[[");}public void print(){System.outl.print("我是StringDisplay");}public void close(){System.outl.print("]]");}
}//Director类指导行为
public class Director{private Builder builder;public Dirctor(Builder builder){this.builder = builder;}//其他类来指导public void construct{builder.open();builder.print();builder.close();}
}
10、Factory Method 模式 [工厂方法模式]
抽象出一个工厂抽象类 和一个产品抽象类。
然后再用 工厂实体类 实现 工厂抽象类。
用 产品实体类 实现 产品实体类。
通过工厂去创建这些实体.
public abstract class Product{public abstract void use();
}public abstract class Factory{public final Product create(String owner){Product p = createProduct(owner);registerProduct(p);return p;}public abstract void Product createProduct(String owner);public abstract void void registerProduct(Product product);
}public class IDCardFactory extends Factory{private List owners = new ArrayList();protected Product createProduct(String owner){return new IDCard(owner);}protected Product registerProduct(Product product){owners.add((IDCard)product.getOwner);}/** IDCard 代码略 */
}
没什么好说的。还有一种 简单工厂方法类。
就是没有把 工厂抽象。让用户根据传入参数自动判断生成产品类型。
它并不属于23种设计模式。
11、Abstract Factory 模式 [抽象工厂模式]
仍以之前提到的汽车为例,抽象工厂我认为是将之前提到的各个组件也进行了工厂方法化。
public abstract class Factory{//单例private static Factory instance;public Factory getInsteance();public abstract Engine createEngine();public abstract Wheel createWheel();public abstract Car createCar();
}public abstract class MyCarFactory extends Factory{public Car createCar(){/** 省略 */createWheel();createEngine();}public Wheel createWheel(){/** 省略 */}public Engine createEngine(){/** 省略 */}
}//各个组件皆抽象 其实每个组件都用了 工厂方法模式
public abstract class Car{public Engine engine;public List<Wheel> wheels;
}public abstract class Engine{}public abstract class Wheel{}public class MyCar extends Car{}class MyEngine extends Engine{public String name; //引擎名字
}class MyWheel extends Wheel{public String name; //车轮名字
}
12、Singleton 模式 [单例模式]
感觉单例模式没啥说的。属于看到就能学会那种,不做赘述。
13、Prototype 模式 [原型模式]
如果要生成某个类对象的 副本 可以使用这个方法。
核心是使用java的 对象的 clone 方法。但是 java 的 clone 是浅复制。
建议自己实现 clone 方法。
也没什么好说的。
原型模式主要为了解决的问题是:
1)对象种类繁多,无法将他们整合到同一个类
2)难以根据类生成实例的时候
3)想解耦框架和生成的实例时
14、Composite 模式 [组合模式]
组合模式的目的就是让 容器和内容具有一致性
比如 数据结构中 树的普通节点和 叶子节点都是节点,这就是他们的一致性。
我们这里可以拿文件系统举例
将文件和文件夹 视为同一种 东西(Entry)。可以帮助我们更好的进行遍历,删除,等一些操作。
public abstract class Entry{//这是文件系统的共同操作public abstract String getName();public abstract int getSize();
}public class File extends Entry{private String name;private int size;public String getName(){/** 省略 */}public int getSize(){/** 省略 */}
}// Directory 同理
public class Directory extends Entry{private String name;private ArrayList<Entry> entryList;public String getName(){/** 省略 */}public int getSize(){/** 省略 */}//在 子目录下创建 其他 Entrypublic void add(Entry entry){}
}
15、Proxy 模式 [代理模式]
现在假设有一个打印类
public class Printer{private String str;public void setStr(String str){this.str = str;}public String getStr(){return this.str;}public void print(){System.output.print(str);}
}
假设Printer 的 setStr 和 getStr 经常用到的方法,
print() 对于类的初始化来说是个十分消耗资源的行为
但是在这个程序的生命周期中,有时候会用到,有时候不会用到。
该如何处理它呢?当然是懒加载,让这个类使用的时候再加载。而不是每次都初始化。
public class PrinterProxy{private String str;private Printer real;public void setStr(String str){if(real != null){real.setStr(str);}this.str = str;}public String getStr(){return str;}public void print(){realized();real.print();}public synchronized void realized(){if(real == null){real = new Printer();real.setStr(str);}}
}
把 new Printer() 想象成一个十分消耗系统资源的任务。
如果我们使用 代理模式的话,就将该类的初始化部分推迟到了 print 任务时候。
而其他地方的时候 并不消耗资源。
16、Mediator 模式 [中介者模式]
中间人模式我理解为 对于类似的一组对象行为 使用一个类进行控制。
仍然假设我们 开发游戏
用户 可以操作的单位 有自己本身的单位,和被自己控制的敌方单位, 以及需要满足一定条件的单位
public interface Unit{private int ownerIndex;private int confuseBy;private Mediator mediator;public void setMediator();public void setControlEnable(Boolean flag);public void hasConfuse(int gamerIndex);
}public class Soldier extends Unit{public void setMediator(){/** 省略 */}//状态发生改变需要通知 Game 类public void hasConfuse(int gamerIndex){this.confuseBy = gamerIndex;//通知一些 游戏改变this.mediator.contorlEnableChanged();}
}public interface Mediator(){public void createUnit();public void contorlEnableChanged();
}public class Game implements Mediator(){public void createUnit(){/** 省略 *///创建单位的时候,记得将 game 作为 Mediator 塞进 Unit 里 Unit unit = new Unit();unit.setMediator(this);}//这里处理所有单位是否被控制public void contorlEnableChanged(){if(unit.getOwnerIndex == gamerIndex || unit.getConfuseBy() == gamerIndex){unit.setControlEnable(true);}else if( /** 其他条件 */){unit.setControlEnable(true);}}
}
这里写的比较简单。看似没有Mediator也能写好这些代码。
但是如果游戏单位众多,以及判断条件众多的话,统一在 contorlEnableChanged() 方法里会让代码更好处理一些。
而其他单位通过中间 通知游戏 一些状态的改变也很方便。
17、Observer 模式 [观察者模式]
观察者模式是一种行为型设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象的状态发生变化时,它会通知所有观察者对象,使它们能够自动更新
import java.util.ArrayList;
import java.util.List;class Subject {private List<Observer> observers = new ArrayList<>();private int state;public int getState() {return state;}public void setState(int state) {this.state = state;notifyAllObservers();}public void attach(Observer observer) {observers.add(observer);}public void notifyAllObservers() {for (Observer observer : observers) {observer.update();}}
}abstract class Observer {protected Subject subject;public abstract void update();
}class ConcreteObserver extends Observer {public ConcreteObserver(Subject subject) {this.subject = subject;this.subject.attach(this);}public void update() {System.out.println("State changed to: " + subject.getState());}
}
三、自己可以自由实现,只需要了解大概思路的模式
这部分包含两个模式,其实只是一种思想。我感觉代码可以凭自己主观去实现。
不过最好是用类去实现。
18、Command 模式 [命令模式]
假设你在写文档,写代码,如果感觉自己写了十几行都不想要了怎么办?
那就 ctrl + z 撤回。
假设撤回过多怎么办?
那就 ctrl + y 回退。
这个过程中,必然某个地方保存了我们的行为过程。
如果我们用类去保存这个过程。然后对过程进行回退就能实现这些行为。
这些过程我们可以把它改造为 命令。 我们写一句代码是一个命令,撤回一句代码就是撤回命令。
这就是命令模式。
假设我们要做一个画板工具 可以画点 画直线 画圆
//定义一个 命令接口
//我们的命令多种多样,所以需要有一个父类。
public interface Command{public abstract void execute();
}public class drawPoint implements Command{private Point point;public void execute(){/** 代码略 */}
}public class drawLine implements Command{private Point pointStart;private Point pointEnd;public void execute(){/** 代码略 */}
}public class drawCircle implements Command{private Point point;private int radius;public void execute(){/** 代码略 */}
}//还需要记录整个命令历史
public class MacroCommand{private Stack<Command> commandStack = new Stack();public void execute(){/** 代码略 */}//添加命令public void append(Comand cmd){if(cmd != this){commandList.push(cmd);}}//撤回命令public Command undo(){Command cmd = null;if(!commandList.empty()){cmd = commandList.pop();}return cmd;}
}
注意,我们这个画板 不是类似于 windows 画板一样。
可以参考 mac 的 图片编辑功能。所有的圈,线,并不是立刻和 画板内容放在一起。
而是依然是个个体,方便我们调整移动它们的位置,或者清除它们。
如果是 windwos 的画板的画,就需要我们保存整张图片,然后回退到图片上一步。
19、Interpreter 模式 [解释器模式]
这个模式代码实现起来复杂,但是要讲东西很简单
就是写一个解释器。
比如要控制小车 前进 左转 右转 重复行为。
我们可以使用程序写很长。
但是 我们的代码每次都需要编译。
如果我们能够自己写一个简单语言然后让java去解析成指令控制小车就可以了。
比如
repeat
forward 3
left
forward 3
left
forward 3
right
end
正则表达式 正式用了 解释器模式。
不做过多赘述。
四、需要记住它的特点的模式
20、Bridge 模式 [桥接模式]
桥接模式(Bridge Pattern)是一种结构型设计模式,它通过将抽象部分与实现部分分离,使它们可以独立变化。这样可以避免类的多层次继承带来的复杂性。
假设我们要做一个画图程序
interface DrawAPI {void drawCircle(int radius, int x, int y);
}class RedCircle implements DrawAPI {public void drawCircle(int radius, int x, int y) {System.out.println("Drawing Circle[ color: red, radius: " + radius + ", x: " + x + ", " + y + "]");}
}class GreenCircle implements DrawAPI {public void drawCircle(int radius, int x, int y) {System.out.println("Drawing Circle[ color: green, radius: " + radius + ", x: " + x + ", " + y + "]");}
}abstract class Shape {protected DrawAPI drawAPI;protected Shape(DrawAPI drawAPI) {this.drawAPI = drawAPI;}public abstract void draw();
}class Circle extends Shape {private int x, y, radius;public Circle(int x, int y, int radius, DrawAPI drawAPI) {super(drawAPI);this.x = x;this.y = y;this.radius = radius;}public void draw() {drawAPI.drawCircle(radius, x, y);}
}
这里我们对绘制和 形状进行了分离。当然我们可以使用 模板方法模式解决这个方案。
但是桥接模式 成功将绘制 和 形状定义的代码进行了分离。方便我们进行拓展。
21、Decorator 模式 [装饰器模式]
装饰器模式是一种结构型设计模式,它允许向一个现有的对象添加新的功能,同时又不改变其结构。装饰器模式通过创建一个装饰类来包装原有的类,从而在保持类方法签名完整的前提下,增强或改变类的功能。
interface Shape {void draw();
}class Circle implements Shape {public void draw() {System.out.println("Shape: Circle");}
}abstract class ShapeDecorator implements Shape {protected Shape decoratedShape;public ShapeDecorator(Shape decoratedShape) {this.decoratedShape = decoratedShape;}public void draw() {decoratedShape.draw();}
}class RedShapeDecorator extends ShapeDecorator {public RedShapeDecorator(Shape decoratedShape) {super(decoratedShape);}public void draw() {decoratedShape.draw();setRedBorder(decoratedShape);}private void setRedBorder(Shape decoratedShape) {System.out.println("Border Color: Red");}
}
22、Visitor 模式 [访问者模式]
访问者模式是一种行为型设计模式,它通过为对象结构中的每个元素添加一个访问方法,使得可以在不改变元素类的前提下定义作用于这些元素的新操作。
interface ComputerPart {void accept(ComputerPartVisitor computerPartVisitor);
}class Keyboard implements ComputerPart {public void accept(ComputerPartVisitor computerPartVisitor) {computerPartVisitor.visit(this);}
}interface ComputerPartVisitor {void visit(Keyboard keyboard);
}class ComputerPartDisplayVisitor implements ComputerPartVisitor {public void visit(Keyboard keyboard) {System.out.println("Displaying Keyboard.");}
}
23、Chain of Responsibility 模式 [责任链模式]
abstract class Logger {public static int INFO = 1;public static int DEBUG = 2;public static int ERROR = 3;protected int level;protected Logger nextLogger;public void setNextLogger(Logger nextLogger) {this.nextLogger = nextLogger;}public void logMessage(int level, String message) {if (this.level <= level) {write(message);}if (nextLogger != null) {nextLogger.logMessage(level, message);}}abstract protected void write(String message);
}class ConsoleLogger extends Logger {public ConsoleLogger(int level) {this.level = level;}protected void write(String message) {System.out.println("Standard Console::Logger: " + message);}
}