zoukankan      html  css  js  c++  java
  • 读书笔记-设计模式之禅

    第一章 单一职责原则

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

    不好的设计:

    好的设计:

    • 接口一定要做到单一职责,类的设计尽量做到只有一个原因引起变化
    第二章 里氏替换原则: 如果子类有必要拥有父类的所有方法。那就继承父类,如果有些方法用得到,有些方法用不到,那就别继承了

    只要父类能出现的地方子类就可以出现,而且替换为子类也不会产生任何错误或异常,使用者可能根本就不需要知道是父类还是子类。
    但是,反过来就不行了,有子类出现的地方,父类未必就能适应
    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("
    --造出的第二批人是黑色人种--");
            Human blackHuman = YinYangLu.createHuman(BlackHuman.class);
            blackHuman.getColor();
            blackHuman.talk();
            //第三次造人,火候刚刚好,于是黄色人种产生了
            System.out.println("
    --造出的第三批人是黄色人种--");
            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("
    --造出的第二批人是黑色人种--");
            Human blackHuman = HumanFactory.createHuman(BlackHuman.class);
            blackHuman.getColor();
            blackHuman.talk();
            //第三次造人,火候刚刚好,于是黄色人种产生了
            System.out.println("
    --造出的第三批人是黄色人种--");
            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("
    --造出的第二批人是黑色人种--");
            Human blackHuman = (new BlackHumanFactory()).createHuman();
            blackHuman.getColor();
            blackHuman.talk();
            //第三次造人,火候刚刚好,于是黄色人种产生了
            System.out.println("
    --造出的第三批人是黄色人种--");
            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;
        }
    }
    
    第11章 建造者模式
    通过传入的顺序决定汽车的功能
    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元");
        }
    }
    只要在代理类中增加实现计费的接口,就可以对用户进行计费
    

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

    第13章 原型模式

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

    第14章 中介者模式


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

    第15章 命令模式
    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();
        }
    }
    

    第16章 责任链模式

  • 相关阅读:
    SqlParameter构造函数让人大吃一斤
    ASP.NET的图片上传和显示
    不去琢磨什么CSS后代选择器之类的鸟玩意了
    datatable里添加一个标识列
    提高工作效率
    调试无法命中断点问题
    离DBA还有多远?
    开发守则
    方法或函数也可以用泛型
    母版页访问内容页
  • 原文地址:https://www.cnblogs.com/Baronboy/p/14271434.html
Copyright © 2011-2022 走看看