首页 > 其他 > 详细

设计模式

时间:2021-04-06 21:11:43      阅读:30      评论:0      收藏:0      [点我收藏+]

设计模式

工厂模式

工厂模式主要为创建对象提供接口,可分为三种模式:

  1. 简单工厂模式Simple Factory
  2. 工厂方法模式Factory Method
  3. 抽象工厂模式Abstract Factory

工厂顾名思义就是创建产品,根据产品是具体产品还是具体工厂可分为简单工厂模式和工厂方法模式,根据工厂的抽象程度可分为工厂方法模式和抽象工厂模式。该模式用于封装和管理对象的创建,是一种创建型模式。

简单工厂模式

它由三种角色组成:

1、工厂类角色:这是本模式的核心,含有一定的商业逻辑和判断逻辑。在java中它往往由一个具体类实现。

2、抽象产品角色:它一般是具体产品继承的父类或者实现的接口。在java中由接口或者抽象类来实现。

3、具体产品角色:工厂类所创建的对象就是此角色的实例。在java中由一个具体类实现。

//AbstractProduct
public interface Phone{
  void make();
}
//Product
public class Miphone implements Phone{
  public MiPhone(){
    this.make();
  }
  @Override
  public void make(){
    System.out.println("MIPHONE");
  }
}
public class Iphone implements Phone{
  public IPhone(){
    this.make();
  }
  @Override
  public void make(){
    System.out.println("IPHONE");
  }
}
//Factory
public class PhoneFactory{
  public Phone makePhone(String phoneType){//注意 返回类型为抽象产品角色
    if(phoneType.equalsIgnoreCase("Miphone")){
      return new Miphone();
    }else if(phoneType.equalsIgnoreCase("Iphone")){
      return new Iphone();
    }
    return null;
  }
}
//Demo
public class Demo{
  public static void main(String[] args){
    PhoneFactory factory = new PhoneFactory();
    Phone miphone = factory.makePhone("MiPhone");
    IPhone iphone = (IPhone)factory.makePhone("IPhone");
  }
}

评价:

使用了简单工厂模式后,我们的程序不在"有病",更加符合现实中的情况;而且客户端免除了直接创建产品对象的责任,而仅仅负责"消费"产品

那么对于产品部分来说,它是符合开闭原则的--对扩展开放、对修改关闭;但是工厂部分好像不太理想,因为每增加一款手机,都要在工厂类中增加相应的商业逻辑和判断逻辑,这显自然是违背开闭原则的。

工厂方法模式

和简单工厂模式中工厂负责生产所有产品相比,工厂方法模式将生成具体产品的任务分发给具体的产品工厂。也就是定义一个抽象工厂,其定义了产品的生产接口,但不负责具体的产品,将生产任务交给不同的派生类工厂。这样不用通过指定类型来创建对象了。

1、抽象工厂角色:这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。在java中它由抽象类或者接口来实现。

2、具体工厂角色:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。在java中它由具体的类来实现。

3、抽象产品角色:它是具体产品继承的父类或者是实现的接口。在java中一般有抽象类或者接口来实现。

4、具体产品角色:具体工厂角色所创建的对象就是此角色的实例。在java中由具体的类来实现。

//和简单工厂相同
//AbstractProduct
public interface Phone{
  void make();
}
//Product
public class Miphone implements Phone{
  public MiPhone(){
    this.make();
  }
  @Override
  public void make(){
    System.out.println("MIPHONE");
  }
}
public class Iphone implements Phone{
  public IPhone(){
    this.make();
  }
  @Override
  public void make(){
    System.out.println("IPHONE");
  }
}

//不同
//AbstractFactory
public interface AbstractFactory{
  Phone makePhone();
}
//ConcreteFactory
public class XiaoMiFactory implements AbstractFactory{
  @Override
  public Phone makePhone(){
    return new MiPhone();
  }
}
public class AppleFactory implements AbstractFactory{
  @Override
  public Phone makePhone(){
    return new IPhone();
  }
}
//Demo
public class Demo{
  public static void main(String[] args){
    AbstractFactory miFactory = new XiaoMiFactory();
    AbstractFactory appleFactory = new AppleFactory();
    miFactory.makePhone();
    appleFactory.makePhone();
  }
}

评价:

使用开闭原则来分析下工厂方法模式。当有新的产品产生时,只要按照抽象产品角色、抽象工厂角色提供的合同来生成,那么就可以被客户使用,而不必去修改任何已有的代码。看来,工厂方法模式是完全符合开闭原则的!

抽象工厂模式

上面两种模式不管工厂怎么拆分抽象,都只是针对一类产品Phone(AbstractProduct),如果要生成另一种产品PC,应该怎么表示呢?

抽象工厂模式通过在AbstarctFactory中增加创建产品的接口,并在具体子工厂中实现新加产品的创建,当然前提是子工厂支持生产该产品。否则继承的这个接口可以什么也不干。

角色和工厂方法相同:

抽象工厂角色:这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。在java中它由抽象类或者接口来实现。

具体工厂角色:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。在java中它由具体的类来实现。

抽象产品角色:它是具体产品继承的父类或者是实现的接口。在java中一般有抽象类或者接口来实现。

具体产品角色:具体工厂角色所创建的对象就是此角色的实例。在java中由具体的类来实现。

//和简单工厂相同
//AbstractProduct
public interface Phone{
  void make();
}
//Product
public class Miphone implements Phone{
  public MiPhone(){
    this.make();
  }
  @Override
  public void make(){
    System.out.println("MIPHONE");
  }
}
public class Iphone implements Phone{
  public IPhone(){
    this.make();
  }
  @Override
  public void make(){
    System.out.println("IPHONE");
  }
}
//添加新产品
//AbstractPC
public interface PC{
  void make();
}
//PC
public class MiPC implements PC{
  public MiPC(){
    this.make();
  }
  @Override
  public void make(){
    System.out.println("MIPC");
  }
}
public class MAC implements PC{
  public MAC(){
    this.make();
  }
  @Override
  public void make(){
    System.out.println("MAC");
  }
}

//AbstractFactory 添加PC产品接口
public interface AbstractFactory{
  Phone makePhone();
  PC makePC();
}
//ConcreteFactory 添加PC的制造
public class XiaoMiFactory implements AbstractFactory{
  @Override
  public Phone makePhone(){
    return new MiPhone();
  }
  public PC makePC(){
    return new MiPC();
  }
}
public class AppleFactory implements AbstractFactory{
  @Override
  public Phone makePhone(){
    return new IPhone();
  }
  public PC makePC(){
    return new MAC();
  }
}
//DEMO
public class Demo{
  public static void main(String[] args){
    AbstractFactory miFactory = new XiaoMiFactory();
    AbstractFactory appleFactory = new AppleFactory();
    miFactory.makePhone();
    miFactory.makePC();
    appleFactory.makePhone();
    appleFactory.makePC();
  }
}

单例模式

懒汉式,线程不安全
public class Singleton{
  private static Singleton instance;
  private Singleton(){
  }
  public static Singleton getInstance(){
    if(instance == null){
      instance = new Singleton();
    }
    return instance;
  }
}

评价:

当有多个线程并行调用 getInstance() 的时候,就会创建多个实例。也就是说在多线程下不能正常工作。

懒汉式,线程安全
public class Singleton{
  private static Singleton instance;
  private Singleton(){
  }
  public static synchronized Singleton getInstance(){
    if(instance == null){
      instance = new Singleton();
    }
    return instance;
  }
}

评价:

虽然做到了线程安全,并且解决了多实例的问题,但是它并不高效。因为在任何时候只能有一个线程调用 getInstance() 方法。但是同步操作只需要在第一次调用时才被需要,即第一次创建单例实例对象时。这就引出了双重检验锁。

双重检验锁

使用同步块加锁的方式

为什么两次检查instance == null?

第一次检查时,可能会有多个线程一起进入if,所以需要在同步块里面进行二次检验

为什么使用volatile?

instance = new Singleton()这句,这并非是一个原子操作,事实上在 JVM 中这句话大概做了下面 3 件事情。

  1. 给 instance 分配内存
  2. 调用 Singleton 的构造函数来初始化成员变量
  3. 将instance对象指向分配的内存空间(执行完这步 instance 就为非 null 了)

但是在 JVM 的即时编译器中存在指令重排序的优化。也就是说上面的第二步和第三步的顺序是不能保证的,最终的执行顺序可能是 1-2-3 也可能是 1-3-2。如果是后者,则在 3 执行完毕、2 未执行之前,被线程二抢占了,这时 instance 已经是非 null 了(但却没有初始化),所以线程二会直接返回 instance,然后使用,然后报错。

public class Singleton{
  private volatile static Singleton instance;
  private Singleton(){
    
  }
  public static Singleton getSingleton(){
    if(instance == null){
      synchronized(Singleton.class){
        if(instance == null){
          instance = new Singleton();
        }
      }
    }
    return instance;
  }
}
饿汉式
public class Singleton{
  private static final Singleton instance = new Singleton();
  private Singleton(){
    
  }
  public static Singleton getInstance(){
    return instance;
  }
}

评价:

单例的实例被声明成 static 和 final 变量了,在第一次加载类到内存中时就会初始化,所以创建实例本身是线程安全的。缺点是它不是一种懒加载模式(lazy initialization),单例会在加载类后一开始就被初始化,即使客户端没有调用 getInstance()方法。饿汉式的创建方式在一些场景中将无法使用:譬如 Singleton 实例的创建是依赖参数或者配置文件的,在 getInstance() 之前必须调用某个方法设置参数给它,那样这种单例写法就无法使用了。

静态内部类
public class Singleton{
  private static class SingletonHolder{
    private static final Singleton INSTANCE = new Singleton();
  }
  private Singleton(){
    
  }
  public static final Singleton getInstance(){
    return SingletonHolder.INSTANCE;
  }
}

评价:

这种写法仍然使用JVM本身机制保证了线程安全问题;由于 SingletonHolder 是私有的,除了 getInstance() 之外没有办法访问它,因此它是懒汉式的;同时读取实例的时候不会进行同步,没有性能缺陷;也不依赖 JDK 版本。

枚举
public enum EasySingleton{
  INSTANCE;
}

评价:

我们可以通过EasySingleton.INSTANCE来访问实例,这比调用getInstance()方法简单多了。创建枚举默认就是线程安全的,所以不需要担心double checked locking,而且还能防止反序列化导致重新创建新的对象。

设计模式

原文:https://www.cnblogs.com/GladysJiu/p/14622245.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!