设计模式 - DesignPattern

1.设计模式的分类?
     设计模式分为三大类:
     创建型模式,共五种:工厂方法模式,抽象工厂模式,单例模式,建造者模式,原型模式。
     结构型模式,共七种:适配器模式,装饰模式,代理模式,外观模式,桥接模式,组合模式,享元模式。
     行为型模式,共十一种:策略模式,模板方法模式,观察者模式,迭代子模式,责任链模式,命名模式,
                                        备忘录模式,状态模式,访问者模式,中介者模式,解释器模式。



2.设计模式的六大原则?
     1、开闭原则(Open Close Principle)
          开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,
          实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。
          想要达到这样的效果,我们需要使用接口和抽象类。
     2、里氏代换原则(Liskov Substitution Principle)
          里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。
          里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。
          LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,
          而衍生类也能够在基类的基础上增加新的行为。
          里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。
          而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。
     3、依赖倒转原则(Dependence Inversion Principle)
          这个是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。
     4、接口隔离原则(Interface Segregation Principle)
          这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。降低类之间的耦合度。
     5、迪米特法则(最少知道原则)(Demeter Principle)
          一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。
     6、合成复用原则(Composite Reuse Principle)
          原则是尽量使用合成/聚合的方式,而不是使用继承。



3.单例模式的好处?
     在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在。
     好处:1、某些类创建比较频繁,对于一些大型的对象,这是一笔很大的系统开销。
               2、省去了new操作符,降低了系统内存的使用频率,减轻GC压力。
               3、有些类,如交易所的核心交易引擎,控制着交易流程,如果该类可以创建多个的话,系统完全乱了。
                    (比如一个军队出现了多个司令员同时指挥,肯定会乱成一团)
                    所以只有使用单例模式,才能保证核心交易服务器独立控制整个流程。



4*.写个 Singleton 单例模式?
     // 1、使用一个内部类来维护单例
     public class Singleton {

          private Singleton() {}

          public static Singleton getInstance() {
               return SingletonFactory.instance;
          }

          private static class SingletonFactory {
               private static Singleton instance = new Singleton();
          }
     }

     // 2、创建类的时候同步
     // 只需要在创建类的时候进行同步,所以只要将创建和getInstance()分开,单独为创建加synchronized关键字。
     public class Singleton {

          private Singleton(){}

          private static Singleton instance = null;

          public static Singleton getInstance(){
               if(instance == null){
                    syncInit();
               }
               return instance;
          }

          private static synchronized void syncInit(){
               if(instance == null){
                    instance = new Singleton();
               }
          }
     }

     //-------------------- 解决问题过程 start ----------------------
     step1: 简单的单例类
    public class Singleton { 

        private Singleton() {}

        private static Singleton instance = null; 

        public static Singleton getInstance() { 
            if (instance == null) { 
                instance = new Singleton(); 
            } 
            return instance; 
        }   
    } 
     step2:由于有线程安全问题,加 synchronized
         public static synchronized Singleton getInstance() { 
            if (instance == null) { 
                instance = new Singleton(); 
            } 
            return instance; 
         } 
     step3: synchronized关键字锁住的是这个对象,这样的用法,在性能上会有所下降,因为每次调用getInstance(),都要对对象上锁。
               事实上,只有在第一次创建对象的时候需要加锁,之后就不需要了。
         public static Singleton getInstance() { 
            if (instance == null) { 
                synchronized (instance) { 
                    if (instance == null) { 
                        instance = new Singleton(); 
                    } 
                } 
            } 
            return instance; 
         } 
     step4: 在Java指令中创建对象和赋值操作是分开进行的,也就是说instance = new Singleton();语句是分两步执行的。
               但是JVM并不保证这两个操作的先后顺序,也就是说有可能JVM会为新的Singleton实例分配空间,
               然后直接赋值给instance成员,然后再去初始化这个Singleton实例。
a>A、B线程同时进入了第一个if判断
b>A首先进入synchronized块,由于instance为null,所以它执行instance = new Singleton();
c>由于JVM内部的优化机制,JVM先画出了一些分配给Singleton实例的空白内存,并赋值给instance成员(注意此时JVM没有开始初始化这个实例),然后A离开了synchronized块。
d>B进入synchronized块,由于instance此时不是null,因此它马上离开了synchronized块并将结果返回给调用该方法的程序。
e>此时B线程打算使用Singleton实例,却发现它没有被初始化,于是错误发生了。

    private static class SingletonFactory{           
        private static Singleton instance = new Singleton();           
    }           
    public static Singleton getInstance(){           
        return SingletonFactory.instance;           
    } 
               单例模式使用内部类来维护单例的实现,JVM内部的机制能够保证当一个类被加载的时候,这个类的加载过程是线程互斥的。
               这样当我们第一次调用getInstance的时候,JVM能够帮我们保证instance只被创建一次,并且会保证把赋值给instance的内存初始化完毕。              
               同时该方法也只会在第一次调用的时候使用互斥机制,这样就解决了低性能问题。
               不过, 如果在构造函数中抛出异常,实例将永远得不到创建,也会出错。
     //-------------------- 解决问题过程  end ----------------------




5. 类的静态方法,也能实现单例模式的效果,有什么不同?
     1、 静态类不能实现接口。(从类的角度说是可以的,但是那样就破坏了静态了。
          因为接口中不允许有static修饰的方法,所以即使实现了也是非静态的)
     2、 单例可以被延迟初始化,静态类一般在第一次加载是初始化。之所以延迟加载,是因为有些类比较庞大,所以延迟加载有助于提升性能。
     3、 单例类可以被继承,他的方法可以被覆写。但是静态类内部方法都是static,无法被覆写。
     4、 单例类比较灵活,毕竟从实现上只是一个普通的Java类,只要满足单例的基本需求,你可以在里面随心所欲的实现一些其它功能,
          但是静态类不行。



6*.写出观察者模式,完整代码,UML图。
      当一个对象变化时,其它依赖该对象的对象都会收到通知,并且随着变化。( 类和类之间的关系,不涉及到继承)
     
    public interface Observer { 
        public void update(); 
    } 
     
    public class Observer1 implements Observer { 
        @Override 
        public void update() { 
            System.out.println("observer1 has received!"); 
        } 
    } 

    public class Observer2 implements Observer { 
        @Override 
        public void update() { 
            System.out.println("observer2 has received!"); 
        } 
    } 

     public interface Subject { 
         /*增加观察者*/ 
         public void add(Observer observer); 

         /*删除观察者*/ 
         public void del(Observer observer); 

         /*通知所有的观察者*/ 
         public void notifyObservers(); 

         /*自身的操作*/ 
         public void operation(); 
     }

    public abstract class AbstractSubject implements Subject { 

        private Vector<Observer> vector = new Vector<Observer>(); 

        @Override 
        public void add(Observer observer) { 
            vector.add(observer); 
        } 

        @Override 
        public void del(Observer observer) { 
            vector.remove(observer); 
        } 

        @Override 
        public void notifyObservers() { 
            Enumeration<Observer> enumo = vector.elements(); 
            while(enumo.hasMoreElements()){ 
                enumo.nextElement().update(); 
            } 
        } 
    } 

    public class MySubject extends AbstractSubject { 
        @Override 
        public void operation() { 
            System.out.println("update self!"); 
            notifyObservers(); 
        } 
    } 

    public class ObserverTest { 

        public static void main(String[] args) { 
            Subject sub = new MySubject(); 
            sub.add(new Observer1()); 
            sub.add(new Observer2()); 

            sub.operation(); 
        } 
    } 




7*.适配器模式,装饰模式,代理模式 之间的区别?分别适用什么场景?
     适配器模式: 适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。
     应用场景:
          1、类的适配器模式:当希望将一个类转换成满足另一个新接口的类时,可以使用类的适配器模式,
                                   创建一个新类,继承原有的类,实现新的接口即可。
          2、对象的适配器模式:当希望将一个对象转换成满足另一个新接口的对象时,可以创建一个Wrapper类,持有原类的一个实例,
                                        在Wrapper类的方法中,调用实例的方法就行。
          3、接口的适配器模式:当不希望实现一个接口中所有的方法时,可以创建一个抽象类Wrapper,实现所有方法,别的类继承抽象类即可。

     装饰模式: 给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例。
     应用场景:
          1、需要扩展一个类的功能。
          2、动态的为一个对象增加功能,而且还能动态撤销。(继承不能做到这一点,继承的功能是静态的,不能动态增删。)
          缺点:产生过多相似的对象,不易排错!

     代理模式: 多一个代理类出来,替原对象进行一些操作。比如租房中介、律师。
     应用场景:
          如果已有的方法在使用的时候需要对原有的方法进行改进,此时有两种办法:
          1、修改原有的方法来适应。这样违反了“对扩展开放,对修改关闭”的原则。
          2、就是采用一个代理类调用原有的方法,且对产生的结果进行控制。
          可以将功能划分的更加清晰,有助于后期维护。



8*.工厂模式与 IoC 模式的区别?
     数据模型:
     public interface Human {   
          void eat();   
          void walk();   
     } 

     public class Chinese implements Human {     
          public void eat() {   
               System.out.println("中国人吃");   
          }     
     public void walk() {   
          System.out.println("中国人行");   
          }   
     }

     public class American implements Human {     
          public void eat() {   
               System.out.println("美国人吃");   
          }     
          public void walk() {   
               System.out.println("美国人行");   
          }   
     }   
      工厂模式:
     public class Factory {   
          public final static String CHINESE = "Chinese";   
          public final static String AMERICAN = "American";   

          public Human getHuman(String str) {   
               if (str.equals(CHINESE))   
                    return new Chinese();   
               else if (str.equals(AMERICAN))   
                    return new American();   
               else 
                    throw new IllegalArgumentException("错误");   
          }   
     }

     public class FactoryTest {   
          public static void main(String[] args) {   
               Human human = null;   
               human = new Factory().getHuman(Factory.CHINESE);   
               human.eat();   
               human.walk();   
               human = new Factory().getHuman(Factory.AMERICAN);   
               human.eat();   
               human.walk();   
          }   
     }
     IoC 模式:
    <?xml version="1.0" encoding="UTF-8"?>   
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">   
    <beans>   
         <bean id="Chinese" class="com.Chinese"/>   
         <bean id="American" class="com.American"/>   
    </beans>   

     public class IoCTest {   
          public final static String CHINESE = "Chinese";   
          public final static String AMERICAN = "American";   

          public static void main(String[] args) {     
               ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");   
               Human human = null;   
               human = (Human) ctx.getBean(CHINESE);   
               human.eat();   
               human.walk();   
               human = (Human) ctx.getBean(AMERICAN);   
               human.eat();   
               human.walk();   
          }   
     }   
     ApplicationContext 相当于原来的 Factory 工厂。
     区别:
          1、如果用户需求发生变化,要把 Chinese 类修改一下。那么工厂模式,就要更改 Factory 类的方法,并且重新编译布署。
               而 IoC 只需 要将 class 属性改变一下,并且由于 IoC 利用了 Java 反射机制,这些对象是动态生成的,
               就可以热插拨 Chinese 对象(不必把原程序停止 下来重新编译布署)
          2、工厂模式 ,调用者无须关心被调用者具体实现过程,只需要找到符合某种标准(接口)的实例,即可使用。
               此时调用的代码面向接口编程,可以让调用者和被调用者解耦。但调用者需要自己定位工厂,调用者与特定工厂耦合在一起。
               IoC 模式 ,调用者无须自己定位工厂,程序运行到需要被调用者时,系统自动提供被调用者实例。
               调用者和被调用者都处于Spring的管理下,二者之间的依赖关系由Spring提供。



9.工厂模式创建对象和 new 创建对象的区别?
     区别:
          new 违反了迪米特法则,耦合度太高。
          工厂模式,耦合度大大降低。并且可以扩展,以后只需要再增加一个工厂类的实现就可以。
          无论是灵活性还是稳定性都得到了极大的提高。



10.静态工厂方法

          
public interface Sender { 
public void Send(); 

public class MailSender implements Sender { 
    @Override 
    public void Send() { 
        System.out.println("this is mailsender!"); 
    } 
}

    public class SmsSender implements Sender { 
        @Override 
        public void Send() { 
            System.out.println("this is sms sender!"); 
        } 
    } 

public class SendFactory { 
    public static Sender produceMail(){ 
        return new MailSender(); 
    } 
    public static Sender produceSms(){ 
        return new SmsSender(); 
    } 
}

    public class FactoryTest { 
        public static void main(String[] args) {     
            Sender sender = SendFactory.produceMail(); 
            sender.Send(); 
        } 
    } 




11.抽象工厂方法
     工厂方法模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了闭包原则。
     抽象工厂模式,创建多个工厂类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。
     

public interface Sender { 
    public void Send(); 
}

public class MailSender implements Sender { 
    @Override 
    public void Send() { 
        System.out.println("this is mailsender!"); 
    } 
}

public class SmsSender implements Sender { 
    @Override 
    public void Send() { 
        System.out.println("this is sms sender!"); 
    } 
}

public interface Provider { 
    public Sender produce(); 
}

    public class SendMailFactory implements Provider { 
        @Override 
        public Sender produce(){ 
            return new MailSender(); 
        } 
    } 

public class SendSmsFactory implements Provider{ 
    @Override 
    public Sender produce() { 
        return new SmsSender(); 
    } 
}

public class Test { 
    public static void main(String[] args) { 
        Provider provider = new SendMailFactory(); 
        Sender sender = provider.produce(); 
        sender.Send(); 
    } 
}




12.原型模式
      将一个对象作为原型,对其进行复制、克隆,产生一个和原对象类似的新对象。
     浅复制:将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型,指向的还是原对象所指向的。
     深复制:将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。简单来说,就是深复制进行了完全彻底的复制,而浅复制不彻底。
public class Prototype implements Cloneable, Serializable { 

    private static final long serialVersionUID = 1L; 
    private String string; 

    private SerializableObject obj; 

    /* 浅复制 */ 
    public Object clone() throws CloneNotSupportedException { 
        Prototype proto = (Prototype) super.clone(); 
        return proto; 
    } 

    /* 深复制 */ 
    public Object deepClone() throws IOException, ClassNotFoundException { 

        /* 写入当前对象的二进制流 */ 
        ByteArrayOutputStream bos = new ByteArrayOutputStream(); 
        ObjectOutputStream oos = new ObjectOutputStream(bos); 
        oos.writeObject(this); 

        /* 读出二进制流产生的新对象 */ 
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); 
        ObjectInputStream ois = new ObjectInputStream(bis); 
        return ois.readObject(); 
    } 

    public String getString() { 
        return string; 
    } 

    public void setString(String string) { 
        this.string = string; 
    } 

    public SerializableObject getObj() { 
        return obj; 
    } 

    public void setObj(SerializableObject obj) { 
        this.obj = obj; 
    } 


class SerializableObject implements Serializable { 
    private static final long serialVersionUID = 1L; 
}




13.适配器模式
     
     类的适配器模式:
           有一个Source类,拥有一个方法,待适配,目标接口时Targetable,通过Adapter类,将Source的功能扩展到Targetable里。
          
public class Source { 
    public void method1() { 
        System.out.println("this is original method!"); 
    } 
}

public interface Targetable { 
    /* 与原类中的方法相同 */ 
    public void method1(); 
    /* 新类的方法 */ 
    public void method2(); 
}

public class Adapter extends Source implements Targetable { 
    @Override 
    public void method2() { 
        System.out.println("this is the targetable method!"); 
    } 
}

public class AdapterTest { 
    public static void main(String[] args) { 
        Targetable target = new Adapter(); 
        target.method1(); 
        target.method2(); 
    } 
}

     对象的适配器模式:
          将Adapter类作修改,不继承Source类,而是持有Source类的实例,以达到解决兼容性的问题。
          
public class Wrapper implements Targetable { 
    private Source source; 

    public Wrapper(Source source){ 
        super(); 
        this.source = source; 
    } 
    @Override 
    public void method2() { 
        System.out.println("this is the targetable method!"); 
    } 
    @Override 
    public void method1() { 
        source.method1(); 
    } 

public class AdapterTest { 
    public static void main(String[] args) { 
        Source source = new Source(); 
        Targetable target = new Wrapper(source); 
        target.method1(); 
        target.method2(); 
    } 
}


     接口的适配器模式:
           有时我们写的一个接口中有多个抽象方法,当我们写该接口的实现类时,必须实现该接口的所有方法,这明显有时比较浪费,因为并不是所有的方法都是我们需要的,有时只需要某一些,此处为了解决这个问题,我们引入了接口的适配器模式,借助于一个抽象类,该抽象类实现了该接口,实现了所有的方法,而我们不和原始的接口打交道,只和该抽象类取得联系,所以我们写一个类,继承该抽象类,重写我们需要的方法就行。
          
public interface Sourceable { 
    public void method1(); 
    public void method2(); 
}

    public abstract class Wrapper2 implements Sourceable{ 
        public void method1(){} 
        public void method2(){} 
    } 

    public class SourceSub1 extends Wrapper2 { 
        public void method1(){ 
            System.out.println("the sourceable interface's first Sub1!"); 
        } 
    } 

public class SourceSub2 extends Wrapper2 { 
    public void method2(){ 
        System.out.println("the sourceable interface's second Sub2!"); 
    } 
}

public class WrapperTest { 
    public static void main(String[] args) { 
        Sourceable source1 = new SourceSub1(); 
        Sourceable source2 = new SourceSub2(); 
        source1.method1(); 
        source1.method2(); 
        source2.method1(); 
        source2.method2(); 
    } 
}


类的适配器模式:当希望将一个类转换成满足另一个新接口的类时,可以使用类的适配器模式,创建一个新类,继承原有的类,实现新的接口即可。
对象的适配器模式:当希望将一个对象转换成满足另一个新接口的对象时,可以创建一个Wrapper类,持有原类的一个实例,在Wrapper类的方法中,调用实例的方法就行。
接口的适配器模式:当不希望实现一个接口中所有的方法时,可以创建一个抽象类Wrapper,实现所有方法,我们写别的类的时候,继承抽象类即可




14.装饰模式
      给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例。
     
    public interface Sourceable { 
        public void method(); 
    } 

public class Source implements Sourceable { 
    @Override 
    public void method() { 
        System.out.println("the original method!"); 
    } 
}

public class Decorator implements Sourceable { 
    private Sourceable source; 

    public Decorator(Sourceable source){ 
        super(); 
        this.source = source; 
    } 
    @Override 
    public void method() { 
        System.out.println("before decorator!"); 
        source.method(); 
        System.out.println("after decorator!"); 
    } 
}

public class DecoratorTest { 
    public static void main(String[] args) { 
        Sourceable source = new Source(); 
        Sourceable obj = new Decorator(source); 
        obj.method(); 
    } 
}
     应用场景:
          1、需要扩展一个类的功能。
          2、动态的为一个对象增加功能,而且还能动态撤销。(继承不能做到这一点,继承的功能是静态的,不能动态增删。)
     缺点:产生过多相似的对象,不易排错!



15.代理模式
      多一个代理类出来,替原对象进行一些操作。
     
public interface Sourceable { 
    public void method(); 
}

public class Source implements Sourceable { 
    @Override 
    public void method() { 
        System.out.println("the original method!"); 
    } 
}

public class Proxy implements Sourceable { 
    private Source source; 

    public Proxy(){ 
        super(); 
        this.source = new Source(); 
    } 
    @Override 
    public void method() { 
        before(); 
        source.method(); 
        after(); 
    } 
    private void after() { 
        System.out.println("after proxy!"); 
    } 
    private void before() { 
        System.out.println("before proxy!"); 
    } 
}

    public class ProxyTest { 
        public static void main(String[] args) { 
            Sourceable source = new Proxy(); 
            source.method(); 
        } 
    } 
     应用场景:
          如果已有的方法在使用的时候需要对原有的方法进行改进,此时有两种办法:
          1、修改原有的方法来适应。这样违反了“对扩展开放,对修改关闭”的原则。
          2、就是采用一个代理类调用原有的方法,且对产生的结果进行控制。这种方法就是代理模式。
     使用代理模式,可以将功能划分的更加清晰,有助于后期维护!








16.模板方法模式
      一个抽象类中,有一个主方法,再定义1...n个方法,可以是抽象的,也可以是实际的方法,定义一个类,继承该抽象类,重写抽象方法,通过调用抽象类,实现对子类的调用。
     
public abstract class AbstractCalculator { 
    /*主方法,实现对本类其它方法的调用*/ 
    public final int calculate(String exp,String opt){ 
        int array[] = split(exp,opt); 
        return calculate(array[0],array[1]); 
    } 

    /*被子类重写的方法*/ 
    abstract public int calculate(int num1,int num2); 

    public int[] split(String exp,String opt){ 
        String array[] = exp.split(opt); 
        int arrayInt[] = new int[2]; 
        arrayInt[0] = Integer.parseInt(array[0]); 
        arrayInt[1] = Integer.parseInt(array[1]); 
        return arrayInt; 
    } 
}

public class Plus extends AbstractCalculator { 
    @Override 
    public int calculate(int num1,int num2) { 
        return num1 + num2; 
    } 
}

public class StrategyTest { 
    public static void main(String[] args) { 
        String exp = "8+8"; 
        AbstractCalculator cal = new Plus(); 
        int result = cal.calculate(exp, "\\+"); 
        System.out.println(result); 
    } 
}














































































  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值