二十三种设计模式第一天:工厂类模式和单例类模式

从今天开始学习java常见的二十三种设计模式,每天2种,天晴下雨,雷打不动。

第一天:

           工厂类模式和单例类模式

首先来看一下java设计中应当遵循的原则:

1、开闭原则(Open Close Principle)

  开闭原则主要意思是 对扩展开放,对修改关闭。当我们需要根据自己的需求实现一些额外的功能时,只能对原始的东西进行扩展,尽量不能对其进行改动,一旦修改,可能就会涉及到功能的失效。而扩展时往往通过继承和复用来实现。

2、里氏代换原则(Liskov Substitution Principle)

里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。只有达到这个效果,才算是真正实现了继承基类的效果。

3、依赖倒转原则(Dependence Inversion Principle)

这个是开闭原则的基础,具体内容:真对接口编程,依赖于抽象而不依赖于具体。

4、接口隔离原则(Interface Segregation Principle)

这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。虽然实现的接口增多了,但是能减少他们之间的耦合度,便于扩展和维护。

5、迪米特法则(最少知道原则)(Demeter Principle)

为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。这些设计原则,其实很像软件的设计原则:松耦合,高内聚。软件都是实现了一定功能的,同理,组成软件的各部分代码也都实现了一定的功能,只是我们将其称作模块、基类、接口等等。

6、合成复用原则(Composite Reuse Principle)

原则是尽量使用合成/聚合的方式,而不是使用继承。

一:工厂模式

工厂模式的主要思想类似于:生活中我们需要某个产品时,我们只需要到对应生产这个产品的工厂去找,不需要我们自己动手去造,虽然可能我们能造这个产品,但不如去工厂取来得便利。java中,实例对应了产品,工厂为我们造实例并返回。如何实现这个实例factory

举例:发送QQ和发送微信消息都为发送消息的行为,他们实现了Send类

interface Send{

  void send();
}

class SendWx implements Send{

   @Override
 void send(){
  
   System.out.print("sendWx");
}
}

class SendQq implements Send{

   @Override
 void send(){
  
   System.out.print("sendQq");
}
}

创建工厂类

class SendFactory{
  public  static Send getSendQq(){
       return new SendQq();
   }
 public  static Send getSendWx(){
       return new SendWx();
   }
 
}

将工厂类中方法申明为静态的,不用实例化工厂类。

以下为调用

class Test{
  public static void main(String[] args){
     SendFactory factory= new SendFactory();
     Send sendQq=factory.getSendQq(); 
    sendQq.send();  
   }
 
}

输出为sendQq;

但是当我们想要添加一个新的 发送功能的时候,我们就必须去修改SendFactory,这里违背了开闭原则,因此我们将SendFactory作为实现了一个工厂接口的子类。

工厂类修改如下:

interface Factory{
   Send getSend();
}


class QqFactory implements Factory{
    Send getSend(){
      return new SendQq();

}

class WxFactory implements Factory{
    Send getSend(){
      return new SendWx();

}

下面为调用

class Test{
  public static void main(String[] args){
     Factory factory= new QqFactory();
     Send sendQq=factory.getSend(); 
    sendQq.send();  
   }
 
}

总体上来讲,我们在有大量的类需要创建,并且它们属于同一种类的类时,我们可以使用工厂模式来设计。。

二 、单例类模式

 在很多时候,我们的项目中会使用到单例类,他有下面一些优点:

1.只会被创建一次,减少了系统开销

2.有时候系统中一个类只能出现一个,如状态同步类、控制类

实现如下:

大致方法是私有化构造方法,使得外部无法实例化,内部使用静态方法返回该类的实例

单例类的创建实际上分为饱汉式和饿汉式。饿汉式是指一来就创建该类,即下面private static Singleton instance = null;改为private static Singleton instance = new Singleton().此处讲解饱汉式。

public class Singleton {  
  
    /* 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载 */  
    private static Singleton instance = null;  
  
    /* 私有构造方法,防止被实例化 */  
    private Singleton() {  
    }  
  
    /* 静态工程方法,创建实例 */  
    public static Singleton getInstance() {  
        if (instance == null) {  
            instance = new Singleton();  
        }  
        return instance;  
    }  
 
    public Object readResolve() {  
        return instance;  
    }  
}  

可是在多线程中,这个程序有可能同时有几个线程进入,创建是不安全的。我们加入syncronized进行互斥。

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

为什么不把syncronized加在静态方法上呢,因此比起互斥一个方法,互斥一个变量的开销要小。并且,加在方法上时,每次获取实例都需要马上对方法加锁,加在变量上时,我先判断是否已经创建了实例,再进行加锁操作,减少了加锁的频率。

但是,这样的情况,还是有可能有问题的,看下面的情况:在Java指令中创建对象和赋值操作是分开进行的,也就是说instance = new Singleton();语句是分两步执行的,分别是为分配空间和初始化实例。但是JVM并不保证这两个操作的先后顺序,也就是说有可能JVM会为新的Singleton实例分配空间,然后直接赋值给instance成员,然后再去初始化这个Singleton实例。这样就可能出错了,我们以A、B两个线程为例:

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实例,却发现它没有被初始化,于是错误发生了。

所以程序还是有可能发生错误,其实程序在运行过程是很复杂的,从这点我们就可以看出,尤其是在写多线程环境下的程序更有难度,有挑战性。我们对该程序做进一步优化:

public class Singleton {  
  
    /* 私有构造方法,防止被实例化 */  
    private Singleton() {  
    }  
  
    /* 此处使用一个内部类来维护单例 */  
    private static class SingletonFactory {  
        private static Singleton instance = new Singleton();  
    }  
  
    /* 获取实例 */  
    public static Singleton getInstance() {  
        return SingletonFactory.instance;  
    }  
  
    /* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */  
    public Object readResolve() {  
        return getInstance();  
    }  
}  

在虚拟机中,它能保证一个类在加载时,一定是互斥进行了,因此采用了内部类来实现。内部类在加载时一定是安全的。

单例模式的使用场景:系统中只需要一个类,来储存一些唯一统一的变量;唯一控制类。

学习和写作参考:https://blog.csdn.net/doymm2008/article/details/13288067

猜你喜欢

转载自blog.csdn.net/make__It/article/details/81145978