设计模式之创建模式

创建模式

创建模式的种类:单例模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式。
1.单例模式
(1)饿汉式:
定义:确保一个类最多只有一个实例,并提供一个全局访问点
单例模式可以分为:饿汉式、懒汉式、静态内部类、枚举类
public class Hungry{
    private final static Hungry HUNGRY=new Hungry();
    public static Hungry getInstance(){
       return HUNGRY;
    }
}
很明显,没有使用该单例对象,该对象就被加载到内存,会造成内存的浪费。
(2)懒汉式:
为了避免内存的浪费,可以采用懒汉式,即用到该单例对象的时候在创建。
public class LazyMan{
     private static LazyMan instance=null;
     private LazyMan(){
     };
     public static LazyMan getInstance(){
           if(instance==null){
                  instance=new LazyMan();
           }
           return instance;
     }
}

(3)单例模式和线程安全
 1. 饿汉式只有一条语句return HUNGRY,这显然可以保证线程安全。但是,饿汉式会造成内存浪费。
 2. 懒汉式不浪费内存,但是无法保证线程的安全。首先,if判断以及其内存执行代码是非原子性的。
其次,new LazyMan()无法保证执行的顺序性。
不满足原子性或者顺序性,线程肯定是不安全的。
创建一个对象,一般分为3步:
(1)初始化内存空间,分配内存空间。
(2)执行构造方法,初始化对象。
(3)把这个对象指向这个空间。
线程不安全举例:
创建两个线程分别为线程A和线程B,创建的过程分别为(1),(2),(3)步。
线程A在创建的过程中,执行的顺序可能是(1)(3)(2)
这样在B线程在B1处判断出instance不为空,线程B就会访问instance引用的对象。此时,线程B将会访问到一个还未初始化的对象。(线程不安全)
保证懒汉式加载的线程安全
1.首先想到就是使用synchronize关键字。synchronize加载getInstance()函数确保了线程的安全。但是,如果要经常的调用getInstance()方法,不管有没有初始化实例,都会唤醒和阻塞线程。为了避免线程上下切换消耗大量时间,如果对象已经实例化,就不用再使用synchronize加锁,直接返回对象。
public class LazyMan{
   private static LazyMan instance=null;
   private LazyMan(){
   };
   public static synchronized LazyMan getInstance(){
   if(instance==null){
        instance=new LazyMan();
     }
     return instance;
   }
}
把sychronized加在if(instance==null)判断语句里面,保证instance未实例化的时候才加锁
public class LazyMan{
     private static LazyMan instance=null;
     private LazyMan(){
     };
     public static synchronized LazyMan getInstance(){
     if(instance==null){
         synchronized(LazyMan.class){
             if(instance==null){
                 instance=new LazyMan();
             }
         }
     }
     return instance;
     }
}
通过上面的讨论知道new一个对象,无法保证顺序性,因此,需要使用另一个关键字volatile保证对象实例过程的顺序性。
public class LazyMan{
   private static volatile LazyMan instance=null;
   private LazyMan(){
   };
   public static synchronized LazyMan getInstance(){
     if(instance==null){
         synchronized(instance){
             if(instance==null){
                instance=new LazyMan();
             }
         }
     }
     return instance;
   }
}
(4)静态内部类:不安全
public class Holder{
   private Holder(){
   }
   public static Holder getInstance(){
       return InnerClass.HOLDER;
   }
   public static class InnerClass{
      private static final Holder HOLDER=new Holder();
   }
}
单例不安全,因为反射可以破坏单例
因此有枚举
public enum EnumSingle{
   INSTANCE;
   public EnumSingle getInstance(){
      return INSTANCE;
   }
}
class Test{
   public static void main(String[] args){
     EnumSingle instance1=EnumSingle.INSTANCE;
     EnumSingle instance2=EnumSingle.INSTANCE;
     System.out.println(instance1);
     System.out.println(instance2);
   }
}

2.工厂模式

2.1 简单工程模式
定义:定义了一个创建对象的类,由这个类来封装实例化对象的行为。
pizza工厂一共生产三种类型的pizza:chesse,pepper,greak.通过工厂类(SimplePizzaFactory)实例化这三种类型的对象。

在这里插入图片描述

public class SimplementPizzaFactory{
   public Pizza CreatePizza(String ordertype){
       Pizza pizza=null;
       if(ordertype.equals("cheess")){
           pizza=new CheesePizza();
       }else if(ordertype.equals("greek")){
           pizza=new GreekPizza();
       }else if(ordertype.equals("pepper")){
           pizza=new PepperPizza();
       }
       return pizza;
   }
}

简单工厂存在的问题与解决方法:简单工厂模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了开闭原则。 我们可以定义一个创建对象的抽象方法并创建多个不同的工厂类实现该抽象方法,要增加新的功能,直接增加新的工厂类就可以,不需要修改之前的代码。

2.2 工厂方法模式
定义:定义了一个创建对象的抽象方法,由子类决定要实例化的类。工厂方法模式将对象的实例化推迟到子类
举例:接着上一个例子,pizza产地有两个:伦敦和纽约。
添加了一个新的产地,如果用简单工厂模式的话,要去修改工厂代码,并且会增加一堆if else语句。工厂方法模式克服了简单工厂要修改代码的缺点,会直接创建两个工厂,纽约工厂和伦敦工厂。

在这里插入图片描述
OrderPizza 中有个抽象的方法:

abstract Pizza createPizza();

两个工厂类继承OrderPizza并实现抽象方法:

public class LDOrderPizza extends OrderPizza{
    Pizza createPizza(String ordertype){
       Pizza pizza=null;
       if(ordertype.equals("chess")){
           pizza=new LDCheesePizza();
       }else if(ordertype.equals("pepper")){
           pizza=new LDPepperPizza();
       }
       return pizza;
    }
}
public class NYOrderPizza extends OrderPizza{
    Pizza createPizza(String ordertype){
       Pizza pizza=null;
       if(ordertype.equals("cheese")){
          pizza=new NYCheesePizza();
       }else if(ordertype.equals("pepper")){
       pizza=new NYPepperPizza();
       }
       return pizza;
    }
}

通过不同的工厂会得到不同的实例化的对象,PizzaStroe的代码如下:

public class PizzaStroe{
   public static void main(String[] args){
      OrderPizza mOrderPizza;
      mOrderPizza=new NYOrderPizza();
   }
}

解决简单工厂模式的问题:增加一个新的pizza产地,只增加一个BJOrderPizza类:

public class BJOrderPizza extends OrderPizza{
   Pizza createPizza(String ordertype){
       Pizza pizza=null;
       if(ordertype.equals("chess")){
           pizza=new LDCheesePizza();
       }else if(ordertype.equals("pepper")){
           pizza=new LDPepperPizza();
       }
       return pizza;
   }
}
工厂方法存在的问题与解决方法:客户端需要创建类的具体实例。简单来说就是用户要订纽约工厂的披萨,必须去纽约工厂,想订伦敦工厂,就必须去伦敦工厂。当伦敦工厂和纽约工厂发生变化时,用户也要跟着变化,就增加了操作的复杂性。为解决这一问题,可以把工厂类抽象为接口,用户只需要去找默认的工厂提出自己的需求,便能得到自己想要的产品,不用根据产品去寻找不同的工厂,方便用户操作。
2.3 抽象工厂模式
定义:定义了一个接口用于创建相关或有依赖关系的对象族,而无需明确指定具体类

在这里插入图片描述

public interface AbsFactory{
    Pizza CreatePizza(String ordertype);
}

工厂的实现

public class LDFactory implements AbsFactory{
   @Override
   public Pizza CreatePizza(String ordertype){
      Pizza pizza=null;
      if("cheese".equals(ordertype)){
       pizza=new LDCheesePizza();
      }else if("pepper".equals(ordertype)){
        pizza=new LDPepperPizza();
      }
      return pizza;
   }
}

PizzaStroe的代码如下

public class PizzaStroe{
    public static void main(String[] args){
       OrderPizza mOrderPizza;
       mOrderPizza=new OrderPizza("London");
    }
}

解决了工厂方法模式的问题:在抽象工厂中PizzaStroe中只需要传入参数就可以实例化对象
工厂模式适用的场合:大量的产品需要创建,并且这些产品具有共同的接口。

三种工厂模式的使用选择
简单工厂:用来生产同一等级结构中的任意产品。(不支持拓展增加产品,固定产品)
工厂方法:用来生产同一等级结构中的固定产品。(支持拓展增加产品)
抽象工厂:用来生产不同产品族的全部产品。(支持拓展增加产品;支持增加产品族)
所以说抽象工厂就像工厂,而工厂方法则像是工厂的一种产品生产线。可以用抽象工厂模式创建工厂,而用工厂方法模式创建生产线。比如可以使用抽象工厂模式创建伦敦工厂和纽约工厂,使用工厂方法实现cheese pizza和greak pizza的生产。
总结:
简单工厂模式就是建立一个实例化对象的类,在该类中对对多个对象实例化。工厂方法模式是定义了一个创建对象的抽象方法,由子类决定要实例化的类。这样做的好处是在有新的类型的对象需要实例化只要增加子类即可。抽象工厂模式定义了一个接口用于创建对象族,而无需明确指定具体类。抽象工厂也是把对象的实例化交给了子类,即支持拓展。同时提供给客户端接口,避免了用户直接操作子类工厂。

3.建造者模式

定义:封装一个复杂对象构造过程,并允许按步骤构造
定义解释:将建造者理解为,假设有一个对象需要建立,这个对象由多个组件组合而成,每个组件的建立都比较复杂,但运用组件来建立所需的对象非常简单,所以就可以将构建复杂组件的步骤与运用组件构建对象分离,使用builder模式可以建立
建造者模式结构中包括四种角色

 1. 产品(Product):具体生产器要构造的复杂对象;
 2. 抽象建造者(Bulider):抽象建造者是一个接口,该接口除了为创建一个Product对象的各个组件定义了若干个方法之外,还要定义返回Product对象的方法
 3. 具体生产器(ConcreteBuilder):实现Builder接口的类,具体建造者将实现Builder接口所定义的方法
 4. 指挥者(Director:指挥者是一个类,该类需要含有Builder接口声明的变量。指挥者的职责是复杂向用户提供具体建造器,即指挥者将请求具体生成器来构造用户所需要的Product对象,如果所请求的具体生成器成功的构造出Product对象,指挥者就可以让该具体建造者返回所构造的Product对象。

在这里插入图片描述
举例:建造一台电脑,需要这么几个步骤(1)需要一个主机(2)需要一个显示器(3)需要一个键盘(4)需要一个鼠标
在这里插入图片描述

public abstract class ComputerBuilder{
   protected Computer computer;
   public Computer getComputer(){
      return computer;
   }
   public void buildComputer(){
      computer=new Computer();
      System.out.println("生成了一台电脑");
   }
   public abstract void buildMaster();
   public abstract void buildScreen();
   public abstract void buildKeyboard();
   public abstract void buildMouse();
   public abstract void buildAudio();
}

HPComputerBuilder定义各个组件

public class HPComputerBuilder extends ComputerBuilder{
   @Override
    public void buildMaster() {
        // TODO Auto-generated method stub
        computer.setMaster("i7,16g,512SSD,1060");
        System.out.println("(i7,16g,512SSD,1060)的惠普主机");
    }
    @Override
    public void buildScreen() {
        // TODO Auto-generated method stub
        computer.setScreen("1080p");
        System.out.println("(1080p)的惠普显示屏");
    }
    @Override
    public void buildKeyboard() {
        // TODO Auto-generated method stub
        computer.setKeyboard("cherry 青轴机械键盘");
        System.out.println("(cherry 青轴机械键盘)的键盘");
    }
    @Override
    public void buildMouse() {
        // TODO Auto-generated method stub
        computer.setMouse("MI 鼠标");
        System.out.println("(MI 鼠标)的鼠标");
    }
    @Override
    public void buildAudio() {
        // TODO Auto-generated method stub
        computer.setAudio("飞利浦 音响");
        System.out.println("(飞利浦 音响)的音响");
    }
}

Director类对组件进行组装并生成产品

public class Director{
   private ComputerBuilder computerBuilder;
   public void setComputerBuilder(ComputerBuilder computerBuilder){
       this.computerBuilder=computerBuilder;
   }
   public Computer getComputer(){
      return computerBuilder.getComputer();
   }
   public void constructComputer(){
        computerBuilder.buildComputer();
        computerBuilder.buildMaster();
        computerBuilder.buildScreen();
        computerBuilder.buildKeyboard();
        computerBuilder.buildMouse();
        computerBuilder.buildAudio();
   }
}
优点:
 1. 将一个对象分解为各个组件
 2. 将对象组件的构造封装起来
 3. 可以控制整个对象的生成过程
缺点
 对不同类型的对象需要实现不同的具体构造器的类,大大增加类的数量
建造者模式构建对象的时候,对象通常构建的过程中需要多个步骤,就像例子中先有主机,在有显示屏,在有鼠标,建造者模式的作用就是将这些复杂的构建过程封装起来。工厂模式构建对象的时候就只有一个步骤,调用一个工厂方法就可以生成一个对象

4.原型模式

浅克隆:将一个对象复制后,基本数据类型的变量都会重新建立,而引用类型,指向的还是原对象所指向的。
深克隆:将一个对象复制后,不论是基本数据类型还是引用类型,都是重新创建的。简单来说,就是深克隆进行了完全彻底的复制,而浅克隆不彻底。clone明显是深复制,clone出来的对象是是不能去影响原型对象的
Client:使用者
Prototype:接口(抽象类),声明具备clone能力,例如java中Cloneable接口
ConcretePrototype:具体的原型类
重点在于Prototype接口和Prototype接口的实现类ConcretePrototype
public class Prototype implements Cloneable{
   public Object clone()throws CloneNotSupportedException{
      Prototype proto=(Prototype)super.clone();
      return proto;
   }
}
原型模式的本质就是clone,可以解决构建复杂对象的资源消耗问题,能再某些场景中提升构建对象的效率;还有一个重要的用途就是保护性拷贝,可以通过返回一个拷贝对象的形式,实现只读的限制。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值