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章 责任链模式

  • 相关阅读:
    Vsftpd 3.0.2 正式版发布
    Putdb WebBuilder 6.5 正式版本发布
    SoaBox 1.1.6 GA 发布,SOA 模拟环境
    pynag 0.4.6 发布,Nagios配置和插件管理
    Percona Playback 0.4,MySQL 负荷回放工具
    xombrero 1.3.1 发布,微型 Web 浏览器
    Hypertable 0.9.6.4 发布,分布式数据库
    libmemcached 1.0.11 发布
    CryptoHeaven 3.7 发布,安全邮件解决方案
    Android Activity生命周期
  • 原文地址:https://www.cnblogs.com/Baronboy/p/14271434.html
Copyright © 2011-2022 走看看