23种设计模式之创建型模式(单例、工厂方法、抽象工厂、生成器和原型模式)

概述


    设计模式是针对某一类问题的最优解决方案,是从许多优秀的软件系统中总结出的。

    Java中设计模式(java design patterns)通常有23种。

    模式可以分成3类:创建型、行为型和结构型。

  创建型模式


    创建型模式涉及对象的实例化,特点是不让用户代码依赖于对象的创建或排列方式,避免用户直接使用new创建对象。

    创建型模式有以下5个:

    工厂方法模式、抽象工厂方法模式、生成器模式、原型模式和单例模式。

    行为型模式


   行为型模式涉及怎样合理的设计对象之间的交互通信,以及怎样合理为对..象分配职责,让设计富有弹性,易维护,易复用。

    行为型模式有以下11个:

    责任链模式、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、模板方法模式和访问者模式。

    结构型模式


    结构型模式涉及如何组合类和对象以形成更大的结构,和类有关的结构型模式涉及如何合理使用继承机制;和对象有关的结构型模式涉及如何合理的使用对象组合机制。

    结构型模式有以下7个:

    适配器模式、组合模式、代理模式、享元模式、外观模式、桥接模式和装饰模式。

    模式中涉及的重要角色,会在描述中(加粗字体)介绍出来。下面就逐一介绍。

上述概述截取自网络,通俗易懂。

1.单例模式

Ensure a class only has one instance,and provide a global point of access to it.

保证一个类仅有一个实例,并提供一个访问它的全局访问点

首先理解这段话,抓住其中的重点信息:一个类仅有一个实例并提供全局访问。

从上我们可以抓到几个关键点:

1.既然是一个类的实例,必然需要构造函数。

2.为了保证有且仅有一个实例,需要对构造函数进行私有化。

3.提供全局访问的接口,说明需要有一个function将唯一实例提供给全局。

由此,我们能知道单例模式的实现方式:

//饿汉式(线程安全,调用效率高,但是不能延时加载)

public class ImageLoader{
    private static ImageLoader instance = new ImageLoader;
    private ImageLoader(){}
    public static ImageLoader getInstance(){
        return instance;
    }
}

所谓饿汉式,就是直接创建一个静态对象,不管来多少人调用,都只提供它,呈现饥饿提供的方式,故名饿汉式。

//懒汉式(线程安全,调用效率不高,但是能延时加载)

public class SingletonDemo2 {

    //类初始化时,不初始化这个对象(延时加载,真正用的时候再创建)
    private static SingletonDemo2 instance;

    //构造器私有化
    private SingletonDemo2(){}

    //方法同步,调用效率低
    public static synchronized SingletonDemo2 getInstance(){
        if(instance==null){
            instance=new SingletonDemo2();
        }
        return instance;
    }
}

所谓懒汉式,就是当需要实例对象且没有实例对象的时候才进行创建,否则不创建,以一种很懒的形式处理,故名懒汉式。

单例的使用场景:作为一个喜欢对战类游戏的我来讲,就好比在对局游戏中双方的基地水晶,无论局面如何变化,各自的水晶都只有一个,这里的水晶对象就可以理解为单例的对象。

2.工厂方法模式

Define an interface for creating an object,but let subclasses decide which class to instantiate.Factory Method lets a class defer instantiation to subclassess.

定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。

同上面的处理方式.

首先来品它的定义,抓住它的重点:定义一个接口,作用是创建对象,而且能够让子类自行选择需要实例化的类。

那么我们可以想象一下它的处理方式:

1.有一个接口。

2.由于需要创建对象,自然需要类去实现这个接口。

3.而且子类可以自己决定实例化哪一个类,因此不少于两个类实现同一个接口。

现在来想象一个场景,有一个接口A,它有一个函数funciton。并且有B和C实现这个接口。那么我们可以通过创建不同的B和C的对象,从而调用其对应的function功能。

//演示简单工厂
public class SimpleFactory {
    public static void main(String args[]) throws Exception{
        Factory factory = new Factory();
        factory.produce("PRO5").run();
        factory.produce("PRO6").run();
    }
}
//抽象产品
interface MeizuPhone{
    void run();
}
//具体产品X2
class PRO5 implements MeizuPhone{
    @Override
    public void run() {
        System.out.println("我是一台PRO5");
    }
}
class PRO6 implements MeizuPhone{
    @Override
    public void run() {
        System.out.println("我是一台PRO6");
    }
}
//工厂
class Factory{
    MeizuPhone produce(String product) throws Exception{
        if(product.equals("PRO5"))
            return new PRO5();
        else if(product.equals("PRO6"))
            return new PRO6();
        throw new Exception("No Such Class");
    }
}

上面实现的就是简单工厂模式,它可以自主选择需要创建的对象。但是它有个缺点,就是一旦我需要再添加一个类实现接口,我需要调整很多东西(不仅需要添加类,还要在Factory中添加一层条件判断语句)。

//工厂方法模式
public class FactoryMethod {
    public static void main(String args[]){
        IFactory bigfactory;
        bigfactory = new SmallFactory();
        bigfactory.produce().run();
        bigfactory = new BigFactory();
        bigfactory.produce().run();
    }
}
//抽象产品
interface MeizuPhone{
    void run();
}
//具体产品*2
class PRO5 implements MeizuPhone{
    @Override
    public void run() {
        System.out.println("我是一台PRO5");
    }
}
class MX5 implements MeizuPhone{
    @Override
    public void run() {
        System.out.println("我是一台MX5");
    }
}
interface IFactory{//抽象的工厂
    MeizuPhone produce();
}
//工厂*2
class BigFactory implements IFactory{
    @Override
    public MeizuPhone produce() {
        return new PRO5();
    }
}
class SmallFactory implements IFactory{
    @Override
    public MeizuPhone produce() {
        return new MX5();
    }
}

以上是工厂方法模式,它使用两个厂去生产两件商品。相比简单工厂,它的解耦性强,不会和别的内容产生牵连。

3.抽象工厂模式

 Provide an interface for creating families of related or dependent objects without specifying their concrete classess.

提供一个创建一系列或相互依赖对象的接口,而无须指定他们的具体的类。

同上抓重点:提供一个接口,用于创建一系列对象。

我们大致知道:

1.我们需要一个接口。

2.我们需要多个实现类。

3.我们可以通过这些实现类创建一系列对象。

//抽象工厂模式
public class AbstractFactory {
    public static void main(String args[]){
        IFactory bigfactory = new BigFactory();
        IFactory smallfactory = new BigFactory();
        bigfactory.producePhone().run();
        bigfactory.produceHeadset().play();
        smallfactory.producePhone().run();
        smallfactory.produceHeadset().play();
    }
}
//抽象产品*2
interface Headset{
    void play();
}
//抽象产品
interface MeizuPhone{
    void run();
}
//具体产品*2*2
class PRO5 implements MeizuPhone{
    @Override
    public void run() {
        System.out.println("我是一台PRO5");
    }
}
class MX5 implements MeizuPhone{
    @Override
    public void run() {
        System.out.println("我是一台MX5");
    }
}
class EP21 implements Headset{
    @Override
    public void play() {
        System.out.println("我是一副EP21");
    }
}
class EP30 implements Headset{
    @Override
    public void play() {
        System.out.println("我是一台EP30");
    }
}
//抽象工厂
interface IFactory{
    MeizuPhone producePhone();
    Headset produceHeadset();
}
//具体工厂*2
class BigFactory implements IFactory{
    @Override
    public MeizuPhone producePhone() {
        return new PRO5();
    }
    @Override
    public Headset produceHeadset() {
        return new EP30();
    }
}
//具体工厂*2
class SmallFactory implements IFactory{
    @Override
    public MeizuPhone producePhone() {
        return new MX5();
    }
    @Override
    public Headset produceHeadset() {
        return new EP21();
    }
}

由上面我们可以看到,一个工厂不仅可以生产一中产品,当我们需要生产多个不同的商品时只需调用不同的方法即可,而且添加一种不同的商品,只需在接口中添加一个方法,并在实体类中间继承实现该方法即可。

简述三者区别:

简单工厂模式的工厂是个实体类,它是通过条件控制进行的选择。工厂方法模式是对简单工厂的延伸,它将工厂类抽象化,允许多个工厂对它进行实现,从而对简单工厂模式进行延伸。而且,当工厂方法的出现,会导致工厂对象数量的增加,于是产生了抽象工厂模式,一个抽象工厂不仅仅只能产生一个产品对象,可以减少重复对象的产生。

    工厂模式主要就涉及上面介绍的三种:

  • 简单工厂模式是由一个具体的类去创建其他类的实例,父类是相同的,父类是具体的。
  • 工厂方法模式是有一个抽象的父类定义公共接口,子类负责生成具体的对象,这样做的目的是将类的实例化操作延迟到子类中完成。
  • 抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,而无须指定他们具体的类。它针对的是有多个产品的等级结构。而工厂方法模式针对的是一个产品的等级结构。

4、生成器模式(Builder Pattern)

Separate the construction of a complex object from its representation so that the same construction process can create different representations.

将一个复杂对象的构建与它的表示分离,使同样的构建过程可以创建不同的表示。

首先理解什么叫将复杂对象的构建和表示分离,以及什么叫同样的构建过程可以创建不同的表示。

举个例子,工厂想要生产一个水杯,不管水杯的品种类型或者材质,必须要经历的三个步骤是生产绳子,生产杯盖,生产杯身。但是对于消费者而言是不关注这些步骤的。

首先需要一个Cup类

public class Cup {

    private String string;    //绳子
    private String cap;       //帽子
    private String cupBody;   //杯体

    public void setString(String string) {
        this.string = string;
    }
    public void setCap(String cap) {
        this.cap = cap;
    }
    public void setCupBody(String cupBody) {
        this.cupBody = cupBody;
    }

    public void show() {
        System.out.println("杯子生产完毕");
    }

}

然后需要一个Builder类

public abstract class  Builder {

    protected Cup cup = new Cup();

    public abstract void buildString();

    public abstract void buildCap();

    public abstract void buildCupBody();

    public  Cup getResult() {
        return cup;
    }

}

然后需要一个ClassCup用于实际生产

public class ClassCup extends Builder {

    @Override
    public void buildString() {
        cup.setString("生产绳子...");
        System.out.println("生产绳子...");
    }

    @Override
    public void buildCap() {
        cup.setCap("生产帽子...");
        System.out.println("生产帽子...");
    }

    @Override
    public void buildCupBody() {
        cup.setCupBody("生产杯体...");
        System.out.println("生产杯体...");
    }


}

 然后需要一个Directior类

public class Director {

    private Builder builder;

    public Director(Builder builder) {
        this.builder = builder;
    }

    public void create() {
        builder.buildString();
        builder.buildCap();
        builder.buildCupBody();
        builder.getResult().show();
    }

    public static void main(String[] args) {
        Director d = new Director(new ClassCup());
        d.create();
    }

}

结果

下面在举一个炸鸡汉堡店的例子供给理解:

//创建一个套餐列表
public interface Item {
   public String name();
   public Packing packing();
   public float price();    
}


//打包方式又有不同
public interface Packing {
   public String pack();
}

//有两种打包方式
class Wrapper implements Packing {
 
   @Override
   public String pack() {
      return "Wrapper";
   }
}

public class Bottle implements Packing {
 
   @Override
   public String pack() {
      return "Bottle";
   }
}

//每个套餐都有一个汉堡和一个饮料,汉堡用包装饮料用杯装
public abstract class Burger implements Item {
 
   @Override
   public Packing packing() {
      return new Wrapper();
   }
 
   @Override
   public abstract float price();
}

public abstract class ColdDrink implements Item {
 
    @Override
    public Packing packing() {
       return new Bottle();
    }
 
    @Override
    public abstract float price();
}


//汉堡和饮料的种类有很多
public class ChickenBurger extends Burger {
 
   @Override
   public float price() {
      return 50.5f;
   }
 
   @Override
   public String name() {
      return "Chicken Burger";
   }
}

public class VegBurger extends Burger {
 
   @Override
   public float price() {
      return 25.0f;
   }
 
   @Override
   public String name() {
      return "Veg Burger";
   }
}

public class Coke extends ColdDrink {
 
   @Override
   public float price() {
      return 30.0f;
   }
 
   @Override
   public String name() {
      return "Coke";
   }
}

public class Pepsi extends ColdDrink {
 
   @Override
   public float price() {
      return 35.0f;
   }
 
   @Override
   public String name() {
      return "Pepsi";
   }
}

//列出菜单,包括套餐的内容和价格
import java.util.ArrayList;
import java.util.List;
 
public class Meal {
   private List<Item> items = new ArrayList<Item>();    
 
   public void addItem(Item item){
      items.add(item);
   }
 
   public float getCost(){
      float cost = 0.0f;
      for (Item item : items) {
         cost += item.price();
      }        
      return cost;
   }
 
   public void showItems(){
      for (Item item : items) {
         System.out.print("Item : "+item.name());
         System.out.print(", Packing : "+item.packing().pack());
         System.out.println(", Price : "+item.price());
      }        
   }    
}

//商家自定义套餐种类可供用户选择
public class MealBuilder {
 
   public Meal prepareVegMeal (){
      Meal meal = new Meal();
      meal.addItem(new VegBurger());
      meal.addItem(new Coke());
      return meal;
   }   
 
   public Meal prepareNonVegMeal (){
      Meal meal = new Meal();
      meal.addItem(new ChickenBurger());
      meal.addItem(new Pepsi());
      return meal;
   }
}


//用户点单
public class BuilderPatternDemo {
   public static void main(String[] args) {
      MealBuilder mealBuilder = new MealBuilder();
 
      Meal vegMeal = mealBuilder.prepareVegMeal();
      System.out.println("Veg Meal");
      vegMeal.showItems();
      System.out.println("Total Cost: " +vegMeal.getCost());
 
      Meal nonVegMeal = mealBuilder.prepareNonVegMeal();
      System.out.println("\n\nNon-Veg Meal");
      nonVegMeal.showItems();
      System.out.println("Total Cost: " +nonVegMeal.getCost());
   }
}

//打印凭据
Veg Meal
Item : Veg Burger, Packing : Wrapper, Price : 25.0
Item : Coke, Packing : Bottle, Price : 30.0
Total Cost: 55.0


Non-Veg Meal
Item : Chicken Burger, Packing : Wrapper, Price : 50.5
Item : Pepsi, Packing : Bottle, Price : 35.0
Total Cost: 85.5

总的来说:建造者模式将复杂产品的构建过程封装分解在不同的方法中,使得创建过程非常清晰,能够让我们更加精确的控制复杂产品对象的创建过程,同时它隔离了复杂产品对象的创建和使用,使得相同的创建过程能够创建不同的产品。但是如果某个产品的内部结构过于复杂,将会导致整个系统变得非常庞大,不利于控制,同时若几个产品之间存在较大的差异,则不适用建造者模式,毕竟这个世界上存在相同点大的两个产品并不是很多,所以它的使用范围有限。 

5、原型模式(Prototype Pattern)

Specify the kinds of objects to create using a prototypical instance,and create new objects by copying this prototype.

用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。

通过定义,我们能发现,它通过原型指定待创建对象的种类,并且新创建的对象是通过原型复制而来。

举个例子:我们想要模仿一架直升飞机,当飞机的内部元器件对我们是不可见的时候(私有成员),我们就算模仿的再像,也只是模仿了它的外观,我们模仿出来的东西是不能起飞的。那我们想要模仿一架一模一样的飞机就可以交给飞机,将它克隆一份自己。

//具体原型
class Plane implements Protoype,Cloneable{

    private String name;    //飞机名称
    private String type;       //飞机型号

    private Plane(Plane plane){
        name = plane.getName();
        type = plane.getType();
    }

    public Plane(){
        name = "自由号";
        type = "133";
    }

    public String getName(){
        return name;
    }

    public String getType(){
        return type;
    }

    @Override
    public Object cloneSelf() {
        return new Plane(this);
    }
}

//抽象原型
interface Protoype{
    public Object cloneSelf();
}

//使用效果
public class Test {
    public static void main(String[] args) {
        Plane plane = new Plane();
        System.out.println(plane.toString());
        System.out.println(plane.getName() + "," + plane.getType());
        System.out.println("---------------------------------------");
        Plane clonePlane = (Plane)plane.cloneSelf();
        System.out.println(clonePlane.toString());
        System.out.println(clonePlane.getName() + "," + clonePlane.getType());
    }
}


//结果

Plane@35851384
自由号,133
---------------------------------------
Plane@649d209a
自由号,133




由结果我们可以看到,我们确实复制了一架飞机,因为对象的hash值是不同的。

但是我们还可以看到,对象私有的成员变量也有成功复制下来,说明我们的复制是成功的。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值