读书笔记-设计模式之禅

第一章 单一职责原则

单一职责原则:要求一个接口或类只有一个原因引起变化,也就是一个接口或类只有一个职责,它就负责一件事情
完美的设计: 一个类实现了两个接口,把两个职责融合在一个类中。

不好的设计:

好的设计:

接口一定要做到单一职责,类的设计尽量做到只有一个原因引起变化

第二章 里氏替换原则

如果子类有必要拥有父类的所有方法。那就继承父类,如果有些方法用得到,有些方法用不到,那就别继承了
只要父类能出现的地方子类就可以出现,而且替换为子类也不会产生任何错误或异常,使用者可能根本就不需要知道是父类还是子类。
但是,反过来就不行了,有子类出现的地方,父类未必就能适应
1.子类必须完全实现父类的方法
注意:如果子类不能完整地实现父类的方法,或者父类的某些方法在子类中已经发
生“畸变”,则建议断开父子继承关系,采用依赖、聚集、组合等关系代替继承
例如:有很多枪可以继承枪的接口,但如果是玩具枪就不适合继承枪的接口,因为它不能杀人,脱离了正常的业务逻辑,应该把玩具枪独立出来,脱离继承
2.子类可以有自己的个性

在项目中,采用里氏替换原则时,尽量避免子类的“个性”,一旦子类有“个性”,这个子
类和父类之间的关系就很难调和了,把子类当做父类使用,子类的“个性”被抹杀——委屈了
点;把子类单独作为一个业务来使用,则会让代码间的耦合关系变得扑朔迷离——缺乏类替
换的标准。

第三章 依赖倒置原则

● 高层模块不应该依赖低层模块,两者都应该依赖其抽象;
● 抽象不应该依赖细节;
● 细节应该依赖抽象
就是面向接口编程
稳定性较高的设计,在周围环境频繁变化的时候,依然可以做到“我自岿然不动
依赖倒置原则的本质就是通过抽象(接口或抽象类)使各个类或模块的实现彼此独立不互相影响,实现模块间的松耦合

一个类公开的public属性或方法越多,修改时涉及的面也就越大,变更引起的风险扩散也就越大。
因此,为了保持朋友类间的距离,在设计时需要反复衡量:是否还可以再减少public方法和属性,
是否可以修改为private、package-private(包类型,在类、方法、变量前不加访问权限,则默认为包类型)、protected等访问权限,是否可以加上final关键字等

第四章接口隔离原则: 接口尽量细化,同时接口中的方法尽量少

第五章 迪米特法则: 高内聚低耦合

第六章 开闭原则: 对扩展开放,对修改关闭

第八章 工厂模式

建立人类的实现类
public class BlackHuman implements Human {
    public void getColor(){
        System.out.println("黑色人种的皮肤颜色是黑色的!");
    }
    public void talk() {
        System.out.println("黑人会说话,一般人听不懂。");
    }
}

public class WhiteHuman implements Human {
    public void getColor(){
        System.out.println("白色人种的皮肤颜色是白色的!");
    }
    public void talk() {
        System.out.println("白色人种会说话,一般都是但是单字节。");
    }
}

建立抽象类
public  abstract class AbstractHumanFactory {
    public abstract <T extends Human> T createHuman(Class<T> c);
}

建立工厂类,通过名称的选择,利用反射构建类
public class HumanFactory extends AbstractHumanFactory {
    public <T extends Human> T createHuman(Class<T> c) {
        //定义一个生产的人种
        Human human = null;
        try {
        //产生一个人种
            human = (T) Class.forName(c.getName()).newInstance();
        } catch (Exception e) {
            System.out.println("人种生成错误!");
        }
        return (T) human;
    }
}
使用:
public class NvWa {
    public static void main(String[] args) {
        //声明阴阳八卦炉
        AbstractHumanFactory YinYangLu = new HumanFactory();
        //女娲第一次造人,火候不足,于是白人产生了
        System.out.println("--造出的第一批人是白色人种--");
        Human whiteHuman = YinYangLu.createHuman(WhiteHuman.class);
        whiteHuman.getColor();
        whiteHuman.talk();
        //女娲第二次造人,火候过足,于是黑人产生了
        System.out.println("\n--造出的第二批人是黑色人种--");
        Human blackHuman = YinYangLu.createHuman(BlackHuman.class);
        blackHuman.getColor();
        blackHuman.talk();
        //第三次造人,火候刚刚好,于是黄色人种产生了
        System.out.println("\n--造出的第三批人是黄色人种--");
        Human yellowHuman = YinYangLu.createHuman(YellowHuman.class);
        yellowHuman.getColor();
        yellowHuman.talk();
    }
}

比较通用的工厂模式

1.定义产品抽象类,抽象类可以定义公共方法
public abstract class Product {
    //产品类的公共方法
    public void method1() {
    //业务逻辑处理
    }
    //抽象方法
    public abstract void method2();
}

2.具体产品类
public class ConcreteProduct1 extends Product {
    public void method2() {
        //业务逻辑处理
    }
}

public class ConcreteProduct2 extends Product {
    public void method2() {
        //业务逻辑处理
    }
}
3.抽象创建工厂
public abstract class Creator {
    /*
     * 创建一个产品对象,其输入参数类型可以自行设置
     * 通常为String、Enum、Class等,当然也可以为空
     */
    public abstract <T extends Product> T createProduct(Class<T> c);
}
4.具体的工厂类
public class ConcreteCreator extends Creator {
    public <T extends Product> T createProduct(Class<T> c) {
        Product product = null;
        try {
            product = (Product) Class.forName(c.getName()).newInstance();
        } catch (Exception e) {
        //异常处理
        }
        return (T) product;
    }
}
5.使用
public class Client {
    public static void main(String[] args) {
        Creator creator = new ConcreteCreator();
        Product product = creator.createProduct(ConcreteProduct1.class);
        /*
         * 继续业务处理
         */
    }
}
好处: 
1.易于扩展,如果有新增功能,直接加上实现类,然后从工厂方法获取即可
2.屏蔽产品类,产品如何变化,调用者都不需要关心,如jdbc如果要从oracle切换到mysql只需要修改驱动类的名称即可

工厂模式扩展:

1.缩小为简单工厂

public class HumanFactory {
    public static <T extends Human> T createHuman(Class<T> c) {
        //定义一个生产出的人种
        Human human = null;
        try {
        //产生一个人种
            human = (Human) Class.forName(c.getName()).newInstance();
        } catch (Exception e) {
            System.out.println("人种生成错误!");
        }
        return (T) human;
    }
}
使用方法
public class NvWa {
    public static void main(String[] args) {
        //女娲第一次造人,火候不足,于是白色人种产生了
        System.out.println("--造出的第一批人是白色人种--");
        Human whiteHuman = HumanFactory.createHuman(WhiteHuman.class);
        whiteHuman.getColor();
        whiteHuman.talk();
        //女娲第二次造人,火候过足,于是黑色人种产生了
        System.out.println("\n--造出的第二批人是黑色人种--");
        Human blackHuman = HumanFactory.createHuman(BlackHuman.class);
        blackHuman.getColor();
        blackHuman.talk();
        //第三次造人,火候刚刚好,于是黄色人种产生了
        System.out.println("\n--造出的第三批人是黄色人种--");
        Human yellowHuman = HumanFactory.createHuman(YellowHuman.class);
        yellowHuman.getColor();
        yellowHuman.talk();
    }
}
变化点:去掉继承抽象类,并在createHuman前增加static关键字;工厂类发生变化,也同时引起了调用者NvWa的变化

2.升级为多个工厂类:为每个产品增加一个工厂

public abstract class AbstractHumanFactory {
    public abstract Human createHuman();
}

public class BlackHumanFactory extends AbstractHumanFactory {
    public Human createHuman() {
        return new BlackHuman();
    }
}

public class YellowHumanFactory extends AbstractHumanFactory {
    public Human createHuman() {
        return new YellowHuman();
    }
}
使用方法
public class NvWa {
    public static void main(String[] args) {
        //女娲第一次造人,火候不足,于是白色人种产生了
        System.out.println("--造出的第一批人是白色人种--");
        Human whiteHuman = (new WhiteHumanFactory()).createHuman();
        whiteHuman.getColor();
        whiteHuman.talk();
        //女娲第二次造人,火候过足,于是黑色人种产生了
        System.out.println("\n--造出的第二批人是黑色人种--");
        Human blackHuman = (new BlackHumanFactory()).createHuman();
        blackHuman.getColor();
        blackHuman.talk();
        //第三次造人,火候刚刚好,于是黄色人种产生了
        System.out.println("\n--造出的第三批人是黄色人种--");
        Human yellowHuman = (new YellowHumanFactory()).createHuman();
        yellowHuman.getColor();
        yellowHuman.talk();
    }
}

延迟加载工厂类

public class ProductFactory {
    private static final Map<String, Product> prMap = new HashMap();

    public static synchronized Product createProduct(String type) throws Exception {
        Product product = null;
        //如果Map中已经有这个对象
        if (prMap.containsKey(type)) {
            product = prMap.get(type);
        } else {
            if (type.equals("Product1")) {
                product = new ConcreteProduct1();
            } else {
                product = new ConcreteProduct2();
            }
        //同时把对象放到缓存容器中
            prMap.put(type, product);
        }
        return product;
    }
}
通过定义一个Map容器,容纳所有产生的对象,如果在Map容器中已经有的对象,则直接取出返回;
如果没有,则根据需要的类型产生一个对象并放入到Map容器中,以方便下次调用

延迟加载框架是可以扩展的,例如限制某一个产品类的最大实例化数量,可以通过判断
Map中已有的对象数量来实现,这样的处理是非常有意义的,例如JDBC连接数据库,都会
要求设置一个MaxConnections最大连接数量,该数量就是内存中最大实例化的数量

第九章 抽象工厂模式

Java的典型类图,一个接口,多个抽象类,然后是N个实现类
抽象工厂模式的通用类图:

public abstract class AbstractProductA {
    //每个产品共有的方法
    public void shareMethod() {
    }
    //每个产品相同方法,不同实现
    public abstract void doSomething();
}

public class ProductA1 extends AbstractProductA {
    public void doSomething() {
        System.out.println("产品A1的实现方法");
    }
}

public class ProductA2 extends AbstractProductA {
    public void doSomething() {
        System.out.println("产品A2的实现方法");
    }
}

public abstract class AbstractProductB {
    //每个产品共有的方法
    public void shareMethod() {
    }
    //每个产品相同方法,不同实现
    public abstract void doSomething();
}

public class ProductB1 extends AbstractProductB {
    public void doSomething() {
        System.out.println("产品B1的实现方法");
    }
}

public class ProductB2 extends AbstractProductB {
    public void doSomething() {
        System.out.println("产品B2的实现方法");
    }
}

public abstract class AbstractCreator {
    //创建A产品家族
    public abstract AbstractProductA createProductA();
    //创建B产品家族
    public abstract AbstractProductB createProductB();
}

public class Creator1 extends AbstractCreator {
    //只生产产品等级为1的A产品
    public AbstractProductA createProductA() {
        return new ProductA1();
    }
    //只生产产品等级为1的B产品
    public AbstractProductB createProductB() {
        return new ProductB1();
    }
}

public class Creator2 extends AbstractCreator {
    //只生产产品等级为2的A产品
    public AbstractProductA createProductA() {
        return new ProductA2();
    }
    //只生产产品等级为2的B产品
    public AbstractProductB createProductB() {
        return new ProductB2();
    }
}

public class Client {
    public static void main(String[] args) {
        //定义出两个工厂
        AbstractCreator creator1 = new Creator1();
        AbstractCreator creator2 = new Creator2();
        //产生A1对象
        AbstractProductA a1 = creator1.createProductA();
        //产生A2对象
        AbstractProductA a2 = creator2.createProductA();
        //产生B1对象
        AbstractProductB b1 = creator1.createProductB();
        //产生B2对象
        AbstractProductB b2 = creator2.createProductB();
        /*
         * 然后在这里就可以为所欲为了...
         */
    }
}

在场景类中,没有任何一个方法与实现类有关系,对于一个产品来说,我们只要知道它
的工厂方法就可以直接产生一个产品对象,无须关心它的实现类

第十章 模板方法模式

public abstract class AbstractClass {
    //基本方法
    protected abstract void doSomething();
    //基本方法
    protected abstract void doAnything();
    //模板方法
    public void templateMethod() {
        /*
         * 调用基本方法,完成相关的逻辑
         */
        this.doAnything();
        this.doSomething();
    }
}

public class ConcreteClass1 extends AbstractClass {
    //实现基本方法
    protected void doAnything() {
        //业务逻辑处理
    }

    protected void doSomething() {
        //业务逻辑处理
    }
}

public class ConcreteClass2 extends AbstractClass {
    //实现基本方法
    protected void doAnything() {
        //业务逻辑处理
    }

    protected void doSomething() {
        //业务逻辑处理
    }
}

public class Client {
    public static void main(String[] args) {
        AbstractClass class1 = new ConcreteClass1();
        AbstractClass class2 = new ConcreteClass2();
        //调用模板方法
        class1.templateMethod();
        class2.templateMethod();
    }
}

模板方法模式的优点:
封装不变部分,扩展可变部分
提取公共部分代码,便于维护
行为由父类控制,子类实现
使用场景:
● 多个子类有公有的方法,并且逻辑基本相同时。
● 重要、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能则由各个子类实现。
● 重构时,模板方法模式是一个经常使用的模式,把相同的代码抽取到父类中,然后通过钩子函数约束其行为。

通过父类中定义钩子函数,约束父类的行为,在子类中重写父类方法就可以改变模板中的行为
public abstract class HummerModel {
    /*
     * 首先,这个模型要能够被发动起来,别管是手摇发动,还是电力发动,反正
     * 是要能够发动起来,那这个实现要在实现类里了
     */
    protected abstract void start();
    //能发动,还要能停下来,那才是真本事
    protected abstract void stop();
    //喇叭会出声音,是滴滴叫,还是哔哔叫
    protected abstract void alarm();
    //引擎会轰隆隆的响,不响那是假的
    protected abstract void engineBoom();
    //那模型应该会跑吧,别管是人推的,还是电力驱动,总之要会跑
    final public void run() {
        //先发动汽车
        this.start();
        //引擎开始轰鸣
        this.engineBoom();
        //要让它叫的就是就叫,喇嘛不想让它响就不响
        if(this.isAlarm()){
            this.alarm();
        }
        //到达目的地就停车
        this.stop();
    }
    //钩子方法,默认喇叭是会响的
    protected boolean isAlarm(){
        return true;
    }
}

public class HummerH1Model extends HummerModel {
    private boolean alarmFlag = true; //要响喇叭
    protected void alarm() {
        System.out.println("悍马H1鸣笛...");
    }
    protected void engineBoom() {
        System.out.println("悍马H1引擎声音是这样的...");
    }
    protected void start() {
        System.out.println("悍马H1发动...");
    }
    protected void stop() {
        System.out.println("悍马H1停车...");
    }
    protected boolean isAlarm() {
        return this.alarmFlag;
    }
    //要不要响喇叭,是由客户来决定的
    public void setAlarm(boolean isAlarm){
        this.alarmFlag = isAlarm;
    }
}

第十一章 建造者模式

通过传入的顺序决定汽车的功能
public abstract class CarModel {
    //这个参数是各个基本方法执行的顺序
    private ArrayList<String> sequence = new ArrayList<String>();
    //模型是启动开始跑了
    protected abstract void start();
    //能发动,还要能停下来,那才是真本事
    protected abstract void stop();
    //喇叭会出声音,是滴滴叫,还是哔哔叫
    protected abstract void alarm();
    //引擎会轰隆隆地响,不响那是假的
    protected abstract void engineBoom();

    //那模型应该会跑吧,别管是人推的,还是电力驱动,总之要会跑
    final public void run() {
        //循环一边,谁在前,就先执行谁
        for (int i = 0; i < this.sequence.size(); i++) {
            String actionName = this.sequence.get(i);
            if (actionName.equalsIgnoreCase("start")) {
                this.start(); //启动汽车
            } else if (actionName.equalsIgnoreCase("stop")) {
                this.stop(); //停止汽车
            } else if (actionName.equalsIgnoreCase("alarm")) {
                this.alarm(); //喇叭开始叫了
            } else if (actionName.equalsIgnoreCase("engine boom")) {
                //如果是engine boom关键字
                this.engineBoom(); //引擎开始轰鸣
            }
        }
    }
    //把传递过来的值传递到类内
    final public void setSequence(ArrayList sequence) {
        this.sequence = sequence;
    }
}
实现类:
public class BenzModel extends CarModel {
    protected void alarm() {
        System.out.println("奔驰车的喇叭声音是这个样子的...");
    }
    protected void engineBoom() {
        System.out.println("奔驰车的引擎是这个声音的...");
    }
    protected void start() {
        System.out.println("奔驰车跑起来是这个样子的...");
    }
    protected void stop() {
        System.out.println("奔驰车应该这样停车...");
    }
}

public class BMWModel extends CarModel {
    protected void alarm() {
        System.out.println("宝马车的喇叭声音是这个样子的...");
    }
    protected void engineBoom() {
        System.out.println("宝马车的引擎是这个声音的...");
    }
    protected void start() {
        System.out.println("宝马车跑起来是这个样子的...");
    }
    protected void stop() {
        System.out.println("宝马车应该这样停车...");
    }
}
这样两个汽车就能根据不同的顺序执行
public class Client {
    public static void main(String[] args) {
        /*
         * 客户告诉XX公司,我要这样一个模型,然后XX公司就告诉我老大
         * 说要这样一个模型,这样一个顺序,然后我就来制造
         */
        BenzModel benz = new BenzModel();
        //存放run的顺序
        ArrayList<String> sequence = new ArrayList<String>();
        sequence.add("engine boom"); //客户要求,run的时候先发动引擎
        sequence.add("start"); //启动起来
        sequence.add("stop"); //开了一段就停下来
        //我们把这个顺序赋予奔驰车
        benz.setSequence(sequence);
        benz.run();
    }
}
如果某一天说要做出启动顺序不同的奔驰车,可以直接修改sequence即可,可以满足不同的需求

  • 通用源码
产品类:
public class Product {
    public void doSomething(){
      //独立业务处理
    }
}
抽象建造者
public abstract class Builder {
    //设置产品的不同部分,以获得不同的产品
    public abstract void setPart();
    //建造产品
    public abstract Product buildProduct();
}
具体建造者
public class ConcreteProduct extends Builder {
    private Product product = new Product();
    //设置产品零件
    public void setPart(){
        /*
         * 产品类内的逻辑处理
         */
    }
    //组建一个产品
    public Product buildProduct() {
        return product;
    }
}
导演类
public class Director {
    private Builder builder = new ConcreteProduct();
    //构建不同的产品
    public Product getAProduct(){
        builder.setPart();
        /*
         * 设置不同的零件,产生不同的产品
         */
        return builder.buildProduct();
    }
}


使用场景:
相同的方法,不同的执行顺序,产生不同的事件结果时,可以采用建造者模式
多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时,则可以使用该模式
产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能,这个时候使用建造者模式非常合适

第十二章 代理模式

通用源码

public interface Subject {
    //定义一个方法
    public void request();
}

public class RealSubject implements Subject {
    //实现方法
    public void request() {
    //业务逻辑处理
    }
}

public class Proxy implements Subject {
    //要代理哪个实现类
    private Subject subject = null;
    //默认被代理者
    public Proxy(){
        this.subject = new Proxy();
    }
    public Proxy(Subject _subject){
        this.subject = _subject;
    }
    //通过构造函数传递代理者
    public Proxy(Object...objects ){
    }
    //实现接口中定义的方法
    public void request() {
        this.before();
        this.subject.request();
        this.after();
    }
    //预处理
    private void before(){
    //do something
    }
    //善后处理
    private void after(){
    //do something
    }
}
你要代理谁就产生该代理的实例,然后把被代理者传递进来
给代理类增加一个接口
public interface IProxy {
    //计算费用
    public void count();
}

public class GamePlayerProxy implements IGamePlayer,IProxy {
    private IGamePlayer gamePlayer = null;
    //通过构造函数传递要对谁进行代练
    public GamePlayerProxy(IGamePlayer _gamePlayer){
        this.gamePlayer = _gamePlayer;
    }
    //代练杀怪
    public void killBoss() {
        this.gamePlayer.killBoss();
    }
    //代练登录
    public void login(String user, String password) {
        this.gamePlayer.login(user, password);
    }
    //代练升级
    public void upgrade() {
        this.gamePlayer.upgrade();
        this.count();
    }
    //计算费用
    public void count(){
        System.out.println("升级总费用是:150元");
    }
}
只要在代理类中增加实现计费的接口,就可以对用户进行计费

动态代理:动态代理是在实现阶段不用关心代理谁,而在运行阶段才指定代理哪一个对象。

第十三章 原型模式

不通过new关键字来产生一个对象,而是通过对象复制来实现的模式就叫做原型模式
使用场景:
资源优化场景
性能和安全要求的场景
一个对象多个修改者的场景
注意:拷贝的时候构造函数不会被执行

第十四章 中介者模式


加入了一个中介者作为三个模块的交流核心
MVC框架中的C就是一个中介,将M和V隔离开,协调M和V工作

第十五章 命令模式

public abstract class Command {
    //把三个组都定义好,子类可以直接使用
    protected RequirementGroup rg = new RequirementGroup(); //需求组
    protected PageGroup pg = new PageGroup(); //美工组
    protected CodeGroup cg = new CodeGroup(); //代码组
    //只有一个方法,你要我做什么事情
    public abstract void execute();
}

public class AddRequirementCommand extends Command {
    //执行增加一项需求的命令
    public void execute() {
        //找到需求组
        super.rg.find();
        //增加一份需求
        super.rg.add();
        //给出计划
        super.rg.plan();
    }
}

public class DeletePageCommand extends Command {
    //执行删除一个页面的命令
    public void execute() {
        //找到页面组
        super.pg.find();
        //删除一个页面
        super.rg.delete();
        //给出计划
        super.rg.plan();
    }
}

public class Invoker {
    //什么命令
    private Command command;
    //客户发出命令
    public void setCommand(Command command){
        this.command = command;
    }
    //执行客户的命令
    public void action(){
        this.command.execute();
    }
}

public class Client {
    public static void main(String[] args) {
        //定义我们的接头人
        Invoker xiaoSan = new Invoker(); //接头人就是小三
        //客户要求增加一项需求
        System.out.println("------------客户要求增加一项需求---------------");
        //客户给我们下命令来
        Command command = new AddRequirementCommand();
        //接头人接收到命令
        xiaoSan.setCommand(command);
        //接头人执行命令
        xiaoSan.action();
    }
}

第十六章 责任链模式

posted @ 2021-01-13 13:01  余***龙  阅读(110)  评论(0编辑  收藏  举报