美文网首页
设计模式学习

设计模式学习

作者: Snail127 | 来源:发表于2018-07-23 23:21 被阅读0次

    编程是一门技术,更是一门艺术

    面向对象:可维护 、可复用、可扩展、灵活性好

    [TOC]

    • 设计模式六大原则

    • 单一职责原则

      单一职责原则:就一个类而言,应该仅有一个引起他变化的原因。
      如果一个类里面承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者一直这个类完成其他职责的能力。这种耦合会导致脆弱的设计当变化发生时,设计会遭到意想不到的破坏。
      软件设计真正要做的许多内容,就是发现职责并把那些职责相互分离。如果能够想到多一个机动类去改变一个类,那么这个类就具有多一个的职责。

    • 开放-封闭原则

    开放封闭原则是软件实体(类、模块、函数等)应该可以扩展,但是不可修改。

    开放封闭原则解决 需求改变时保持系统相对稳定,从而使系统在第一个版本以后不断推出新版本。面对需求,对程序的改动是通过增加新代码进行的,而不是更改现有代码。

    开放封闭原则是面向对象的核心开发人员应该仅对程序中呈现出频繁变化的那些部分菹做出抽象,然而,对于应用程序中每个部分都刻意的进行抽象同样不是一个好主意。拒绝不成熟的抽象和抽象本身一样重要。

    • 依赖倒转原则

    *抽象不应该依赖细节,细节因该依赖于抽象。意思是依赖接口编程,不要对实现编程

    依赖倒转原则:

    A. 高层模块不应该依赖底层模块,两个都应该依赖抽象。

    B. 抽象不应该依赖细节,细节应该依赖抽象。

    • 里氏代换原则

    里氏代换原则:子类必须能够替换它们的父类型

    只有当子类可以替换掉父类,软件单位的功能不受影响时,父类才能真正被复用,而子类也能够在父类的基础上增加新的行为。

    • 迪米特法则

    迪米特法则,如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某个方法,可以通过第三者转发这个调用

    迪米特法则在类的结构设计上,每一个类都应当尽量降低成员的访问权限。

    迪米特法则根本思想,是强调了类之间的松耦性,类之间的耦合越弱,越有利于复用,一个处在弱耦合的类被修改,不会对有关系的类造成波及。

    • 接口隔离原则

    //todo: auth:XXX date:XXXX-XX-XX

    • 简单工厂模式

    简单工厂模式的实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类(这些产品类继承自一个父类或接口)的实例。

    //形状类
    public class Shape {
      public void show() {
        System.out.println("shape");
      }
    }
    
    //继承形状类
    public class Square extends Shape {
    
      public Square() {
      }
    
      @Override public void show() {
        System.out.println("square");
      }
    }
    
    public class Circle extends Shape {
    
      public Circle() {
      }
    
      @Override public void show() {
        System.out.println("circle");
      }
    }
    
    /**
     * @author <a href="http://github.com/athc">dujf</a>
     * @date 2018/8/30
     * @since JDK1.8
     * 简单工厂模式
     */
    public class SimpleFactory {
    
      //通过key 来装配 不满足开闭原则
      private static Shape getShape(String key) {
        if ("CIRCLE".equals(key)) {
          return new Circle();
        } else if ("SQUARE".equals(key)) {
          return new Square();
        } else {
          return new Shape();
        }
      }
    
      /**
       * 改进通过 class 装配
       *
       * @param tClass
       * @return
       */
      private static Object getShape(Class<? extends Shape> tClass) {
        Object shape = null;
        try {
    //      shape = tClass.newInstance();
    //反射
          shape = Class.forName(tClass.getName()).newInstance();
        } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
          e.printStackTrace();
        }
        return shape;
      }
    
      public static void main(String[] args) {
        Shape shape = SimpleFactory.getShape("CIRCLE");
        shape.show();
        Shape shape1 = SimpleFactory.getShape("SQUARE");
        shape1.show();
    
        Shape shape2 = (Shape) SimpleFactory.getShape(Circle.class);
        shape2.show();
        Shape shape3 = (Shape) SimpleFactory.getShape(Square.class);
        shape3.show()
      }
    }
    
    • 工厂方法模式

    定义一个用于创建对象的接口,让子类决定实例化哪一个子类。工厂方法使一个类的实例化延迟到其子类。

    工厂方法模式是简单工厂的仅一步深化, 在工厂方法模式中,不再提供一个统一的工厂类来创建所有的对象,而是针对不同的对象提供不同的工厂。也就是说 每个对象都有一个与之对应的工厂 。

    工厂方法实现时,客户端需要决定实例化哪一个工厂来实现运算类,选择判断的问题还是存在的,工厂方法把简单工厂的内部逻辑判断移到了客户端代码来进行。想要加功能本来是改工厂类,现在变成修改客户端代码

      //抽象工厂是在简单工厂模式基础上,为每一个类都创建一个工厂
    
    public interface Factory {
      /**
       * @return
       */
      Shape getShape();
    }
    
    //圆形工厂
    public class CircleFactory implements Factory {
      @Override public Shape getShape() {
        return new Circle();
      }
    }
    
    //工厂方法模式给每一个类都提供一个工厂
        Factory factory = new CircleFactory();
        Shape shape4 = factory.getShape();
        shape4.show();
    
    • 抽象工厂模式

    抽象工厂模式,提供一个创建一系列相关或相互依赖对象的接口,无需指定他们具体的类。

    所有在用简单工厂的地方,都可以考虑用反射技术来去除switch或if,解除分支判断带来的耦合。

    //枪
    public interface Gun {
      void shoot();
    }
    //子弹
    public interface Bullet {
      void load();
    }
    //AK 枪
    public class AkGun implements Gun {
      @Override public void shoot() {
        System.out.println("AK shoot");
      }
    }
    //AK 子弹
    public class AkBullet implements Bullet {
      @Override public void load() {
        System.out.println("load for AK");
      }
    }
    //M4a1 枪
    public class M4a1Gun implements Gun {
      @Override public void shoot() {
        System.out.println("M4A1 shoot");
      }
    }
    //m4a1 子弹
    public class M4a1Bullet implements Bullet {
      @Override public void load() {
        System.out.println("load for M4a1");
      }
    }
    //抽象工厂 创建一系列相关或相互依赖的接口 枪和子弹
    public interface BaseFactory {
      Gun getGun();
      Bullet getBullet();
    }
    //Ak相关工厂类
    public class AkAboutFactory implements BaseFactory {
      @Override public Gun getGun() {
        return new AkGun();
      }
      @Override public Bullet getBullet() {
        return new AkBullet();
      }
    }
    //测试
    public class AbstractFactoryTest {
      public static void main(String[] args) {
        BaseFactory baseFactory = new AkAboutFactory();
        Gun gun = baseFactory.getGun();
        Bullet bullet = baseFactory.getBullet();
        gun.shoot();
        bullet.load();
      }
    }
    
    • 策略模式

    策略模式:定义了家族算法,分别封装起来让它们之间可以相互替换,此模式让算法的变化,不会影响到使用算法的客户

    策略模式是一种定义一系列算法方法的方法,这些算法完成的工作是相同的,仅仅实现不同,它可以以相同的方式调用所有算法,减少了各种算法类与使用算法的耦合。

    策略模式优点:简化了单元测试,

    策略模式是用来封装算法的,只要分析过程中听到不同时间应用不同的业务规则,都能考虑策略模式。

    • 装饰模式

    装饰模式,动态的给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。

    装饰模式是为已有功能动态的添加更多功能的一种方式。

    装饰模式优点:把类中的装饰功能从类中搬移去除,这样可以简化原有的类。有效的把类的核心职责和装饰功能区分开了,而且可以去除相关类中重复的装饰逻辑。

    • 代理模式

    代理模式,为其他对象提供一种代理以控制对这个对象的访问。

    代理分为静态代理和动态代理两种。
    静态代理,代理类需要自己编写代码写成。
    动态代理,代理类通过 Proxy.newInstance() 方法生成。
    不管是静态代理还是动态代理,代理与被代理者都要实现两样接口,它们的实质是面向接口编程。
    静态代理和动态代理的区别是在于要不要开发者自己定义 Proxy 类。
    动态代理通过 Proxy 动态生成 proxy class,但是它也指定了一个 InvocationHandler 的实现类。
    代理模式本质上的目的是为了增强现有代码的功能。

    • 原型模式

    原型模式,用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新对象

    如果字段是值类型的,则对该字段执行逐位复制,如果字段是引用类型,则复制引用但不复制引用的对象,因此,原始对象及其复本引用同一对象。

    浅复制:被复制对象的所有变量含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象。

    深复制:把引用对象的变量指向复制过的新对象,而不是原有的被引用的对象。

    • 模版方法模式

    模版方法模式:定义一个操作中的算法骨架,而将一些步骤延迟到之类中,模版方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

    模版方法模式是通过把不变行为搬移到超类,去除子类中的重复代码来体现它的优势。

    模版方法模式就是提供了一个很好的代码复用平台。

    • 外观模式

    外观模式,为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层的接口,这个接口使得这一子系统更加容易使用

    public interface Base {
      void start();
      void stop();
    }
    
    public class Mouse implements Base {
      @Override public void start() {
        System.out.println("Mouse start");
      }
      @Override public void stop() {
        System.out.println("Mouse stop");
      }
    }
    //将子系统合在一起
    public class Computer implements Base {
      private Mouse mouse;
      private Keyboard keyboard;
      public Computer() {}
      public Computer(Mouse mouse, Keyboard keyboard) {
        this.mouse = mouse;
        this.keyboard = keyboard;
      }
     //省略getter setter 
    ......
      @Override public void start() {
        mouse.start();
        keyboard.start();
      }
      @Override public void stop() {
        mouse.stop();
        keyboard.stop();
      }
    }
    
    public static void main(String[] args) {
        Computer computer = new Computer();
        computer.setComputer(new Computer());
        computer.setMobile(new Mouse());
        computer.start();
        computer.stop();
      }
    

    何时使用外观模式?

    1. 在设计初期阶段,应该有意识将不同的两个层分离,层与层之间建立不同的外观。
    2. 在开发阶段子系统往往因为不断重构演化而变得越来越复杂,增加外观可以提供一个简单的接口,减少它们之间的依赖。
    3. 在维护一个遗留的大型系统时,可能这个系统已经非常难以维护和扩展,为新系统开发一个外观类,来提供设计粗糙或者高度复杂的遗留代码的比较清晰简单的接口,让新系统与外观类对象交互,外观类与遗留代码交互所有复杂的工作。
    • 建造者模式

    建造者模式,将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示

    什么时候使用建造者模式?

    建造者模式主要用于创建一些复杂的对象,这些对象内部构建间的建造顺序通常是稳定的,但对象内部结构通常面临着复杂的变化。建造者模式的好处就是使得建造代码与表示代码分离,由于建造者隐藏了该产品是如何组装的所以若需要改变一个产品内部表示,只需要在定义一个具体的建造者就可以。

    建造者模式是在当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式适合的模式。

    • 观察者模式

    观察者模式,定义一种一对多的依赖关系,让 多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使他们能主动更新自己。

    观察者模式所做的工作其实就是在解除耦合,让耦合的双方都依赖于抽象,而不是依赖于具体类,从而使得各自变化都不会影响另一边的变化。

    委托就是一种引用方法的类型,一旦为委托分配了方法,委托将与该方法具有完全相同的行为,委托方法的使用可以像其他任何方法一样,具有参数和返回值,委托看作是对函数的抽象,是函数的类,委托的实例将代表一个具体的函数。

    一个委托可以搭载多个方法,所有方法被依次唤起,它可以使得委托对象所搭载的方法并不需要属于同一个类。

    委托对象所搭载的所有方法必须具有相同的原型和形式,也就是拥有相同的参数列表和返回值类型。


    面向对象其实就是希望做到代码的责任分解


    • 状态模式

    状态模式,当一个对象的内在状态改变时,允许改变其行为,这个对象看起来像是改变了类

    状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况,把状态的判断逻辑转移到表示不同状态的系列中,可以把复杂的判断逻辑简化。

    状态模式通过把各种状态转移逻辑分布到state的子类之间,来减少相互间的依赖,

    当一个 对象的行为取决于他的状态,并且他必须在运行时刻根据状态改变他的行为时,就可以考虑状态模式。

    • 适配模式

    适配模式,将一个类的接口转换成客户希望的另外一个接口。Adapter 模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作

    适配模式主要应用于希望复用一些现有的类,但是接口有与复用环境要求不一致的情况

    类适配模式和对象适配模式

    • 备忘录模式

    备忘录模式,在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可以将该对象恢复到原先保存的状态

    • 组合模式

    组合模式,将对象组合成树形结构已表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。

    何时使用组合模式?

    需求中式体现部分与整体层次的结构时,以及你希望用户可以忽略组合对象与单个对象的不同时,统一的使用组合结构中的所有对象时,使用组合模式。

    • 适配器模式

    适配器模式,提供一种方法顺序访问一个聚合对象中的各种元素,而又不暴露该对象的内部表示

    一个聚合对象,而且不管这些对象是什么都需要遍历时,考虑使用迭代器模式。

    迭代器模式是为了分离集合对象的遍历行为。抽象出一个迭代器类来负责,这样可以做到不暴露内部结构,又可以让外部代透明的访问集合内部的数据。

    • 单例模式

    单例模式,保证一个类仅有一个实例,并提供一个访问它的全局访问点

    懒汉模式,要在第一次被引用时,才会将自己实例化,线程不安全

    public class Singleton{
        private static Singleton singleton;
        
        public Singleton(){}
        
        public static Singleton getSingleton(){
            if(null==singleton){
                singleton=new Singleton();
            }
            return singleton;
        }
    }
    

    单例模式因为singleton类封装他的唯一实例,这样它可以严格控制客户怎样访问它及合适访问它,简单的说对实例的唯一控制。

    lock是确保当一个线程位于代码临界区时,另一个线程不进入临界区。如果其他线程试图进入锁定代码,则它将一直等待,知道该对象被释放。

    public class Singleton{
        private static Singleton singleton;
        
        private static readonly object syncRoot =new Object();
        
        
        public Singleton(){}
        
        public static Singleton getSingleton(){
            
            lock(syncRoot){
                if(null==singleton){
                    singleton=new Singleton();
                }
            }
            return singleton;
        }
    }
    

    双重锁检测,单例模式

    public class Singleton{
        private static Singleton singleton;
        
        private static readonly object syncRoot =new Object();
     
        public Singleton(){}
        
        public static Singleton getSingleton(){
            if(null ==sington){
                lock(syncRoot){
                    if(null==singleton){
                        singleton=new Singleton();
                    }
                }
            }
            return singleton;
        }
    }
    

    静态初始化,单例模式

    public class Singleton{
        private static Singleton singleton=new Singleton();
        
        public Singleton(){}
        
        public static Singleton getSingleton(){
          
            return singleton;
        }
    }
    

    静态初始化的方式是在自己被加载时就将自己实例化,所以被形象的称为饿汉单例类

    静态内部类实现:

    /**
     * 静态内部类
     */
    public class Singleton {
    
      private static class SingletonHolder {
        private static Singleton singleton = new Singleton();
      }
    
      public Singleton getSingleton() {
        return SingletonHolder.singleton;
      }
    }
    
    • 桥接模式

    桥接模式,将抽象部分与它的实现部分分离是他们都可以独立的变化

    合成/聚合复用原则,尽量使用合成/聚合,尽量不要使用类继承。

    实现指的是抽象类和它的派生类用来实现自己的对象。

    • 命令模式

    命令模式,将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,请求排队或记录请求日志,以及支持可撤销的操作。

    命令模式的优点:

    一、能比较容易的设计一个命令队列

    二、在需要的情况下,可以比较容易的将命令计入日志

    三、允许接收请求的一方决定是否要否决请求

    四、可以容易的实现对请求的撤销和重做

    五、由于加进新的具体命令类不影响其他的类因此增加新的具体命令类很容易

    六、命令模式吧请求一个操作的对象与其知道怎么执行一个操作的对象分割开

    • 责任链模式

    职责链模式:使多个对象有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止

    当客户提交一个请求时,请求是沿链传递直至有一个对象负责处理它,接收者和传送者都没有对方明确的信息,且链中的对象自己也并不知道链的结构。结果是职责链可简化对象的相互谅解,它们仅仅需保持一个指向其后继者的引用,而不需保持它所有的候选接受者的引用。

    随时的增加或修改处理一个请求的结构,增强了给对象指派职责的灵活性

    一个请求极有可能,到了链的末端都得不到处理,或者因为没有正确的配置而得不到处理。

    • 中介者模式

    中介者模式:用一个中介者对象来封装一系列的对象交互,中介者对象不需要显示的相互引用,从而使其耦合松散,而且可以独立的改变它们之间的交互

    中介者模式,很容易在系统应用中误用,当系统出现了多对多交互复杂的对象群时,不要急于使用中介者模式,首先要反思你的系统中在设计上是不是合理。

    由于把对象如何协作进行抽象,将中介者作为独立的概念并将其封装在一个对象中,这样关的对象,就从对象各自本身的行为转移到它们之间的交互上来,也就是站在一个更宏观的角度去看待系统。

    由于控制的集中化,于是就把交互复杂性变为了中介者的复杂性,这就使得中介者会变的比任何一个人都复杂。

    中介者模式一般用于一组对象以及定义良好但是复杂的方式进行通信的场合,以及想制定一个分布在多个类中的行为,而又不想生成太多的子类的场合。

    • 享元模式

    享元模式:运用共享技术有效的支持大量细粒度的对象。

    享元模式可以避免大量非常相似类的开销,在程序设计中有时需要生成大量细粒度的实体类来表示数据,如果能发现这些实例除了几个参数外基本上都是相同的,有时就能够受大幅度地减少需要实例化的类的数量,如果能把那些参数移到类实例的外面,在方法调用时将它们传递进来,就可以通过共享大幅度的减少单个实例的数目。

    如果一个应用程序使用了大量的对象,而大量的这些对象造成了很大的存储开销时就应该考虑使用,还有就是对象的大多数状态可以外部状态,如果删除对象的外部状态,那么就可以用相对较少的共享对象取代很多组对象,此时考虑使用享元模式。

    • 解释模式

    解释器模式:定义一种语言,定义它的文法的一种表示,并定义一种解释器,这个 解释器用该表示来解释语言中的句子。

    如果一种特类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。

    当一个语言需要解释执行,并且你可以将该语言中的句子表示为一个抽象语法树时,可以使用解释器模式。

    解释器模式很容易的改变和扩展文法,因为该模式使用类来表示文法规则,你可以使用继承来改变或扩展该文法,也比较容易实现文法,因为定义抽象语法树中各个节点的类的实现大体相似,这类都易于直接编写。

    解释器模式为文法中的每一条规则至少定义了一个类,因此包含许多规则的文法可能可能难以管管理和维护,建议当文法非常复杂时,使用其他的技术如语法分析程序或编译生成器来处理。

    • 访问者模式

    访问者模式:表示一个作用于某对象结构中的个元素的操作。它使你可以在不改变各元素的类的前提下,定义作用于这些元素的新操作。

    访问者模式适用于数据结构相对稳定的系统,它吧数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对的自由的演化。

    访问者模式主要目的使要把处理从数据结构分离出来,有比较稳定的数据结构,又有益于变化的话,使访问者模式就是比较合适的,因为访问者模式使得算法操作的增加变得容易。

    访问者模式的优点就是增加新的操作很容易,因为怎家新的操作就意味着怎家一个新的访问者,访问者,模式将有关的行为集中到一个访问对象中。

    缺点使增加新的数据结构变得困难。

    相关文章

      网友评论

          本文标题:设计模式学习

          本文链接:https://www.haomeiwen.com/subject/lvusmftx.html