您的位置:首页 > 编程语言 > Java开发

部分设计模式(工厂+单例+适配器+装饰+代理+观察者)

2018-02-26 11:16 323 查看
 
http://blog.csdn.net/a394268045/article/details/51801258
 
1.普通工厂设计模式
 
--实现同一接口的一些类,需要对这些类进行创建—》建立一个工厂类
 
接口:
publicinterface Sender { 
    publicvoid Send(); 
}
 
两个实现类:
publicclassMailSenderimplementsSender { 
    @Override 
    publicvoid Send() { 
        System.out.println("this is mailsender!"); 
    } 
}
 
publicclassSmsSenderimplementsSender { 
 
    @Override 
    publicvoid Send() { 
        System.out.println("this is sms sender!"); 
    } 
}
 
1.1通过传入的字符串建立对象的工厂类
publicclass SendFactory { 
 
    public Sender produce(String type) { 
        if ("mail".equals(type)) { 
            returnnew MailSender(); 
        } elseif ("sms".equals(type)) { 
            returnnew SmsSender(); 
        } else { 
            System.out.println("请输入正确的类型!"); 
            returnnull; 
        } 
    } 

1.2.创建对象时不需要提供字符串,提供方法
publicclass SendFactory { 
   public Sender produceMail(){ 
        returnnew MailSender(); 
    } 
 
    public Sender produceSms(){ 
        returnnew SmsSender(); 
    } 
}
 
1.3静态工厂
将工厂方法设置为静态的,不需要创建实例
publicclass SendFactory { 
 
    publicstatic Sender produceMail(){ 
        returnnew MailSender(); 
    } 
 
    publicstatic Sender produceSms(){ 
        returnnew SmsSender(); 
    } 

 
 
2.抽象工厂模式:
来源:类的创建依赖工厂类,如果要拓展程序,必须修改工厂类,违背了开闭原则。
则采用抽象工厂模式,创建多个工厂类,需要新的功能,直接增加新的工厂类,不需要修改之前的代码
 
首先,还是有一个我们将创建实例类的抽象接口publicinterface Sender {     publicvoid Send(); 
}
 
其次,两个实现类publicclassMailSenderimplementsSender {     @Override    publicvoid Send() {        System.out.println("this is mailsender!");     } }  publicclassSmsSenderimplementsSender {      @Override    publicvoid Send() {        System.out.println("this is sms sender!");     } 

 
再提供工厂的接口:publicinterface Provider {     public Sender produce(); 

 
两个工厂实现类:publicclassSendMailFactoryimplementsProvider {      @Override    public Sender produce(){         returnnew MailSender();     } 

 publicclassSendSmsFactoryimplementsProvider{      @Override    public Sender produce() {         returnnew SmsSender(); 

4000
   } }
 
总结:
抽象工厂设计模式,将工厂也抽象出来,如果现在要增加一个功能,如发及时消息,则需要一个sender的实现类,同时需要一个工厂的实现类(实现provider)无需改动现有的代码。
 
 
3.单例设计模式
---保证JVM中只有该对象的一个实例存在
好处:
一些类的创建比较频繁,开销大
 不使用new,降低内存的使用频率,减轻gc压力
 有些类不能创建多个,如果创建多个则系统乱掉
 
3.1不安全的懒汉式
Public class Singleton{
         Privatestatic Singleton instance = null;//延迟加载
         PrivateSingleton(){};//防止被实例化
         Publicstatic Singleton getInstance(){
         If(instance==null){
 Instance = new Singleton();
}
  Return instance;
}
}
 
 
3.2饿汗式
Public class Singleton{
         Privatestatic Singleton instance = new Singleton();       
Private Singleton(){};//防止被实例化
         Publicstatic Singleton getInstance(){
  Return instance;
}
}
 
3.3.1同步:给getInstance加synchronize关键字,但是效率低publicstaticsynchronized Singleton getInstance() {         if (instance == null) {              instance = new Singleton();         }        return instance; 

3.3.2同步:只在创建对象的时候加锁publicstatic Singleton getInstance() {         if (instance == null) {              synchronized (instance) {                //第二次判断的意义:两个线程在第一次判断后,其中一个线程new完新对象,//第二个线程执行到这里时,不会检测instance是否为null,直接new,//出现问题,所以需要第二次非空判断,保证指向的同一个实例                if (instance == null) {                      instance = new Singleton();                  }             }        }        return instance; 
}
3.3.3同步:实现线程安全
来源:
在java指令中创建对象和赋值操作时分开进行的,instance = new Singleton(),分两步执行,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实例,却发现它没有被初始化,于是错误发生了。
 
解决:使用内部类维护单例的实现,JVM内部的机制能够保证当一个类被加载的时候,这个类的加载过程是线程互斥的。这样当我们第一次调用getInstance的时候,JVM能够帮我们保证instance只被创建一次,并且会保证把赋值给instance的内存初始化完毕,这样我们就不用担心上面的问题。同时该方法也只会在第一次调用的时候使用互斥机制,这样就解决了低性能问题。publicclass Singleton {      /* 私有构造方法,防止被实例化 */    private Singleton() {     }     /* 此处使用一个内部类来维护单例 */    privatestaticclass SingletonFactory {         privatestatic Singleton instance = new Singleton();     }     /* 获取实例 */    publicstatic Singleton getInstance() {         return SingletonFactory.instance;     }     /* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */    public Object readResolve() {         return getInstance();     } 

 
在创建类的时候进行同步:
publicclass SingletonTest { 
 
    private static SingletonTest instance =null; 
 
    private SingletonTest() { 
    } 
 
    private static synchronized void syncInit(){ 
        if (instance == null) { 
            instance = newSingletonTest(); 
        } 
    } 
 
    public static SingletonTest getInstance(){ 
        if (instance == null) { 
            syncInit(); 
        } 
        return instance; 
    } 

 
3.3.4 volatile
当程序在运行过程中,会将运算需要的数据从主存复制一份到CPU的高速缓存当中,那么CPU进行计算时就可以直接从它的高速缓存读取数据和向其中写入数据,当运算结束之后,再将高速缓存中的数据刷新到主存当中。举个简单的例子,比如下面的这段代码:
i= i +
 
1;

 

当线程执行这个语句时,会先从主存当中读取i的值,然后复制一份到高速缓存当中,然后CPU执行指令对i进行加1操作,然后将数据写入高速缓存,最后将高速缓存中i最新的值刷新到主存当中。
  这个代码在单线程中运行是没有任何问题的,但是在多线程中运行就会有问题了。在多核CPU中,每条线程可能运行于不同的CPU中,因此每个线程运行时有自己的高速缓存(对单核CPU来说,其实也会出现这种问题,只不过是以线程调度的形式来分别执行的)。
可能存在下面一种情况:初始时,两个线程分别读取i的值存入各自所在的CPU的高速缓存当中,然后线程1进行加1操作,然后把i的最新值1写入到内存。此时线程2的高速缓存当中i的值还是0,进行加1操作之后,i的值为1,然后线程2把i的值写入内存。
 
最终结果i的值是1,而不是2。这就是著名的缓存一致性问题。通常称这种被多个线程访问的变量为共享变量。
 
可见性问题://线程1执行的代码int i = 0;i = 10; //线程2执行的代码j = i;假若执行线程1的是CPU1,执行线程2的是CPU2。由上面的分析可知,当线程1执行 i =10这句时,会先把i的初始值加载到CPU1的高速缓存中,然后赋值为10,那么在CPU1的高速缓存当中i的值变为10了,却没有立即写入到主存当中。  此时线程2执行 j = i,它会先去主存读取i的值并加载到CPU2的缓存当中,注意此时内存当中i的值还是0,那么就会使得j的值为0,而不是10.  这就是可见性问题,线程1对变量i修改了之后,线程2没有立即看到线程1修改的值。
 
 
 
Volatile变量具有synchronized 的可见性特性,但是不具备原子特性。这就是说线程能够自动发现volatile变量的最新值。
当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。
 
---这里在声明变量时使用了volatile关键字来保证其线程间的可见性;在同步代码块中使用二次检查,以保证其不被重复实例化。集合其二者,这种实现方式既保证了其高效性,也保证了其线程安全性。
publicclass MySingleton { 
     
    //使用volatile关键字保其可见性 
    volatile private static MySingletoninstance = null; 
     
    private MySingleton(){} 
      
    public static MySingleton getInstance(){ 
        try {   
            if(instance != null){//懒汉式  
                 
            }else{ 
                //创建实例之前可能会有一些准备性的耗时工作  
                Thread.sleep(300); 
                synchronized(MySingleton.class) { 
                    if(instance == null){//二次检查 
                        instance = newMySingleton(); 
                    } 
                } 
            }  
        } catch (InterruptedException e) {  
            e.printStackTrace(); 
        } 
        return instance; 
    } 

 
3.3.5静态代码块实现单例
publicclass MySingleton{ 
      
    private static MySingleton instance =null; 
      
    private MySingleton(){} 
 
    static{ 
        instance = new MySingleton(); 
    } 
     
    public static MySingleton getInstance(){  
        return instance; 
    }  

 
 
4.适配器模式:
目的是消除由于接口不匹配所造成的类的兼容性问题
 
类适配器:
核心思想就是:有一个Source类,拥有一个方法,待适配,目标接口时Targetable,通过Adapter类,将Source的功能扩展到Targetable里
 
抽象思维:鼠标以前的插头source,转接口adapter,要想source与adapter连接上,则adpter继承自source,adpter需要提供新的接口,则实现targetable接口。publicclassSource {     publicvoid method1() {         System.out.println("this is original method!");     } 
}publicinterfaceTargetable {     /* 与原类中的方法相同 */    publicvoid method1();     /* 新类的方法 */    publicvoid method2(); 
} publicclassAdapterextendsSourceimplementsTargetable {      @Override    publicvoid method2() {         System.out.println("this is the targetable method!");     } 
}
测试:Targetable target = new Adapter(); target.method1(); 
target.method2(); 
 
 
对象适配器:
基本思路和类的适配器模式相同,只是将Adapter类作修改,这次不继承Source类,而是持有Source类的实例,以达到解决兼容性的问题publicclassWrapperimplementsTargetable {      private Source source;     public Wrapper(Source source){         super();        this.source = source;     }    @Override    publicvoid method2() {         System.out.println("this is the targetable method!");     }     @Override    publicvoid method1() {         source.method1();     } 
}
 
接口适配:
我们也常会遇到这种接口中定义了太多的方法,以致于有时我们在一些实现类中并不是都需要
 publicinterface Sourceable {      publicvoid method1();    publicvoid method2(); 

 publicabstractclassWrapper2implementsSourceable{     publicvoid method1(){}    publicvoid method2(){} 
} publicclassSourceSub1extendsWrapper2 {     publicvoid method1(){        System.out.println("the sourceable interface's first Sub1!");     } 

 publicclassSourceSub2extendsWrapper2 {     publicvoid method2(){        System.out.println("the sourceable interface's second Sub2!");    } 

 
三种适配器的应用场景:
类的适配器模式:当希望将一个类转换成满足另一个新接口的类时,可以使用类的适配器模式,创建一个新类,继承原有的类,实现新的接口即可。
 
对象的适配器模式:当希望将一个对象转换成满足另一个新接口的对象时,可以创建一个Wrapper类,持有原类的一个实例,在Wrapper类的方法中,调用实例的方法就行。
 
接口的适配器模式:当不希望实现一个接口中所有的方法时,可以创建一个抽象类Wrapper,实现所有方法,我们写别的类的时候,继承抽象类即可
 
 
 
5.装饰模式:
装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例。publicinterfaceSourceable {    publicvoid method(); 
} publicclassSourceimplementsSourceable {      @Override    publicvoid method() {        System.out.println("the original method!");     } 

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

 
 
6.代理模式:
代理模式就是多一个代理类出来,替原对象进行一些操作,比如我们在租房子的时候回去找中介,为什么呢?因为你对该地区房屋的信息掌握的不够全面,希望找一个更熟悉的人去帮你做,此处的代理就是这个意思。再如我们有的时候打官司,我们需要请律师,因为律师在法律方面有专长,可以替我们进行操作,表达我们的想法
 
public interface Sourceable { 
public void method(); 
} public classSource implements Sourceable {@Override public voidmethod() {      System.out.println("the originalmethod!");  } } public classProxy implements Sourceable {
private Source source; 
public Proxy(){ 
    super(); 
    this.source = new Source(); 
@Override 
public void method() { 
    before(); 
    source.method(); 
    atfer(); 
private void atfer() { 
    System.out.println("after proxy!"); 
private void before() { 
    System.out.println("before proxy!"); 

7.观察者模式:
类似于邮件订阅和RSS订阅,当我们浏览一些博客或wiki时,经常会看到RSS图标,就这的意思是,当你订阅了该文章,如果后续有更新,会及时通知你。其实,简单来讲就一句话:当一个对象变化时,其它依赖该对象的对象都会收到通知,并且随着变化!对象之间是一种一对多的关系
MySubject类就是我们的主对象,Observer1和Observer2是依赖于MySubject的对象,当MySubject变化时,Observer1和Observer2必然变化。AbstractSubject类中定义着需要监控的对象列表,可以对其进行修改:增加或删除被监控对象,且当MySubject变化时,负责通知在列表内存在的对象。
 
public interfaceObserver { 
public void update(); 

public class Observer1 implements Observer{
@Override 

public void update() { 

    System.out.println("observer1has 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 AbstractSubjectimplements Subject {
private Vector<Observer> vector = newVector<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 extendsAbstractSubject {
@Override 

public void operation() { 

   System.out.println("update self!"); 

   notifyObservers(); 


}
测试:
Subject sub = new MySubject(); 

   sub.add(new Observer1()); 

   sub.add(new Observer2()); 

 

   sub.operation(); 

 
 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java设计模式
相关文章推荐