设计模式——创建型模式

设计模式—创建型模式

创建型模式有以下几种模式:

  • 单例模式
  • 抽象工厂模式
  • 原型模式
  • 建造者模式
  • 工厂模式

注意:其中抽象工厂模式和建造者模式也算是工厂模式

单例模式

单例设计模式介绍

所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)。

比如HibernateSessionFactory,它充当数据存储源的代理,并负责创建Session对象。SessionFactory并不是轻量级的,一般情况下,一个项目通常只需要一个SessionFactory就够,这是就会使用到单例模式。

单例模式的三步曲

  • 构造器私有化 (防止 new )
  • 类的内部创建对象
  • 向外暴露一个静态的公共方法

单例设计模式八种方式

注意:加粗的为推荐使用

  • 饿汉式(静态常量)
  • 饿汉式(静态代码块)
  • 懒汉式(线程不安全)
  • 懒汉式(线程安全,同步方法)
  • 懒汉式(线程安全,同步代码块)
  • 双重检查
  • 静态内部类
  • 枚举

饿汉式(静态常量)

代码实现:

package com.singleton.type1;

public class SingletonTest {
    public static void main(String[] args) {
        Singleton singleton=Singleton.getInstance();
        Singleton singleton2=Singleton.getInstance();
        System.out.println(singleton==singleton2);
        System.out.println("singleton的hashcode:"+singleton.hashCode());
        System.out.println("singleton2的hashcode:"+singleton2.hashCode());


    }
}

//饿汉式(静态常量)
class Singleton{
    //私有构造方法
    private Singleton(){

    }
    //创建对象实例
    private static Singleton instance=new Singleton();

    //对外提供获取对象实例方法
    public static Singleton getInstance(){
        return instance;
    }
}

优缺点:

  • 优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。
  • 缺点:在类装载的时候就完成实例化,没有达到Lazy Loading的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费
  • 这种方式基于classloder机制避免了多线程的同步问题,不过,instance在类装载时就实例化,在单例模式中大多数都是调用getInstance方法, 但是导致类装载的原因有很多种,因此不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化instance就没有达到lazy loading的效果
  • 结论:这种单例模式可用,可能造成内存浪费

饿汉式(静态代码块)

代码实现:

package com.singleton.type2;

public class SingletonTest {
    public static void main(String[] args) {
        Singleton singleton= Singleton.getInstance();
        Singleton singleton2= Singleton.getInstance();
        System.out.println(singleton==singleton2);
        System.out.println("singleton的hashcode:"+singleton.hashCode());
        System.out.println("singleton2的hashcode:"+singleton2.hashCode());


    }
}

//饿汉式(静态代码块)
class Singleton{
    //私有构造方法
    private Singleton(){

    }
    //通过静态代码块创建对象
    private static Singleton instance=null;

    static {
        instance=new Singleton();
    }

    //对外提供获取对象实例方法
    public static Singleton getInstance(){
        return instance;
    }
}

优缺点:

  • 这种方式和静态常量的饿汉式的方式其实类似,只不过将类实例化的过程放在了静态代码块中,也是在类装载的时候,就执行静态代码块中的代码,初始化类的实例。优缺点和上面是一样的。
  • 结论:这种单例模式可用,但是可能造成内存浪费

懒汉式(线程不安全)

代码实现:

package com.singleton.type3;

public class SingletonTest {
    public static void main(String[] args) {
        Singleton singleton= Singleton.getInstance();
        Singleton singleton2= Singleton.getInstance();
        System.out.println(singleton==singleton2);
        System.out.println("singleton的hashcode:"+singleton.hashCode());
        System.out.println("singleton2的hashcode:"+singleton2.hashCode());


    }
}

//懒汉式(线程不安全)
class Singleton{
    //私有构造方法
    private Singleton(){

    }
    //创建空对象
    private static Singleton instance=null;


    //对外提供获取对象实例方法,在调用该方法时,对象才会进行创建
    public static Singleton getInstance(){
        if(instance==null) {
            instance = new Singleton();
        }
        return instance;
    }
}

优缺点:

  • 起到了Lazy Loading的效果,但是只能在单线程下使用。
  • 如果在多线程下,一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。所以在多线程环境下不可使用这种方式
  • 结论:在实际开发中,不要使用这种方式

懒汉式(线程安全,同步方法)

代码实现:

package com.singleton.type4;

public class SingletonTest {
    public static void main(String[] args) {
        Singleton singleton= Singleton.getInstance();
        Singleton singleton2= Singleton.getInstance();
        System.out.println(singleton==singleton2);
        System.out.println("singleton的hashcode:"+singleton.hashCode());
        System.out.println("singleton2的hashcode:"+singleton2.hashCode());


    }
}

//懒汉式(线程安全,同步方法)
class Singleton{
    //私有构造方法
    private Singleton(){

    }
    //创建空对象
    private static Singleton instance=null;


    //对外提供获取对象实例方法,在该方法上加上同步锁,保证线程安全,调用该方法时,对象才会进行创建
    public static synchronized Singleton getInstance(){
        if(instance==null) {
            instance = new Singleton();
        }
        return instance;
    }
}

优缺点:

  • 解决了线程不安全问题
  • 效率太低了,每个线程在想获得类的实例时候,执行getInstance()方法都要进行同步。而其实这个方法只执行一次实例化代码就够了,后面的想获得该类实例,直接return就行了。方法进行同步效率太低
  • 结论:在实际开发中,不推荐使用这种方式

懒汉式(线程安全,同步代码块)

代码实现:

package com.singleton.type5;

public class SingletonTest {
    public static void main(String[] args) {
        Singleton singleton= Singleton.getInstance();
        Singleton singleton2= Singleton.getInstance();
        System.out.println(singleton==singleton2);
        System.out.println("singleton的hashcode:"+singleton.hashCode());
        System.out.println("singleton2的hashcode:"+singleton2.hashCode());


    }
}

//懒汉式(线程安全,同步代码块)
class Singleton{
    //私有构造方法
    private Singleton(){

    }
    //创建空对象
    private static Singleton instance=null;


    //对外提供获取对象实例方法,使用同步代码块方法,保证线程安全,调用该方法时,对象才会进行创建
    public static  Singleton getInstance(){
        if(instance==null) {
            synchronized (Singleton.class) {
                instance = new Singleton();
            }
        }
        return instance;
    }
}

优缺点:

  • 这种方式,本意是想对第四种实现方式的改进,因为前面同步方法效率太低,改为同步产生实例化的的代码块
  • 但是这种同步并不能起到线程同步的作用。跟第3种实现方式遇到的情形一致,假如一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例
  • 结论:在实际开发中,不能使用这种方式

双重检查

代码实现:

package com.singleton.type6;

public class SingletonTest {
    public static void main(String[] args) {
        Singleton singleton= Singleton.getInstance();
        Singleton singleton2= Singleton.getInstance();
        System.out.println(singleton==singleton2);
        System.out.println("singleton的hashcode:"+singleton.hashCode());
        System.out.println("singleton2的hashcode:"+singleton2.hashCode());


    }
}

//双重检查
class Singleton{
    //私有构造方法
    private Singleton(){

    }
    //创建空对象,要加上volatile,保证instance被修改时,立刻更新
    private static volatile Singleton instance=null;


    //对外提供获取对象实例方法,使用双重检查来保证线程安全,调用该方法时,对象才会进行创建
    public static Singleton getInstance(){
        if(instance==null) {
            synchronized (Singleton.class) {
                if (instance==null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

优缺点:

  • 双重检查概念是多线程开发中常使用到的,如代码中所示,我们进行了两次if (singleton == null)检查,这样就可以保证线程安全了。
  • 这样,实例化代码只用执行一次,后面再次访问时,判断if (singleton == null),直接return实例化对象,也避免的反复进行方法同步.
  • 线程安全;延迟加载;效率较高
  • 结论:在实际开发中,推荐使用这种单例设计模式

静态内部类

代码实例:

package com.singleton.type7;

public class SingletonTest {
    public static void main(String[] args) {
        Singleton singleton= Singleton.getInstance();
        Singleton singleton2= Singleton.getInstance();
        System.out.println(singleton==singleton2);
        System.out.println("singleton的hashcode:"+singleton.hashCode());
        System.out.println("singleton2的hashcode:"+singleton2.hashCode());


    }
}

//静态内部类
class Singleton{
    //私有构造方法
    private Singleton(){

    }
    //静态内部类
    public static class SingletonInfo{
        private static final Singleton instance=new Singleton();
    }

    //对外提供获取对象实例方法,调用静态内部类得到对象
    public static Singleton getInstance() {
        return SingletonInfo.instance;
    }
}

优缺点:

  • 这种方式采用了类装载的机制来保证初始化实例时只有一个线程。
  • 静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的
  • 实例化。
  • 类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。
  • 优点:避免了线程不安全,利用静态内部类特点实现延迟加载,效率高
  • 结论:推荐使用.

枚举

代码实例:

package com.singleton.type8;

public class Singleton {

    public static void main(String[] args) {
        SingletonEnum instance=SingletonEnum.INSTANCE;
        SingletonEnum instance2=SingletonEnum.INSTANCE;

        System.out.println(instance.hashCode());
        System.out.println(instance2.hashCode());
        instance.test();
        instance2.test();
    }
}
enum SingletonEnum {
    INSTANCE;
    public void test(){
        System.out.println("嘟嘟嘟");
    }
}

优缺点:

  • 这借助JDK1.5中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。
  • 这种方式是Effective Java作者Josh Bloch 提倡的方式
  • 结论:推荐使用

单例模式在JDK应用的源码分析

单例模式在JDK 应用

我们JDK中,java.lang.Runtime就是经典的单例模式(饿汉式)


单例模式注意事项和细节说明
  • 单例模式保证了 系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能
  • 当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new
  • 单例模式使用的场景:需要频繁的进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多(即:重量级对象),但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session工厂等)

工厂设计模式

案例理解

看一个披萨的项目:要便于披萨种类的扩展,要便于维护:

  • 披萨的种类很多(比如 GreekPizz、CheesePizz 等)
  • 披萨的制作有 prepare,bake, cut, box
  • 完成披萨店订购功能。

传统代码模式

代码实现:

//pizza抽象类
public abstract class Pizza {
    protected String name;

    public void setName(String name) {
        this.name = name;
    }

    public abstract void prepare();

    public void bake(){
        System.out.println(name+"----烤");
    }
    public void cut(){
        System.out.println(name+"----切");
    }
    public void pack(){
        System.out.println(name+"----打包");
    }
}
//pizza实现类
public class GreekPizza extends Pizza{
    @Override
    public void prepare() {
        System.out.println("准备GreekPizza所需的材料");
    }
}
----
public class CheesePizza extends Pizza {
    @Override
    public void prepare() {
        System.out.println("准备CheesePizza所需的材料");
    }
}
//orderPizza类
public class OrderPizza {

    public OrderPizza(){
        String orderType="";
        Pizza pizza=null;
        do {
            orderType=getType();
            if(orderType.equals("c")){
                pizza=new CheesePizza();
                pizza.setName("芝士披萨");
            }else if(orderType.equals("g")){
                pizza=new GreekPizza();
                pizza.setName("夏威夷披萨");
            }else {
                System.out.println("请输入正确的披萨名称");
                break;
            }

            pizza.bake();
            pizza.cut();
            pizza.pack();
        }while (true);
    }

    public String getType() {
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("请输入你要预定的pizza:");
        try {
            String s = bufferedReader.readLine();
            return s;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

}

优缺点:

  • 优点是比较好理解,简单易操作。
  • 缺点是违反了设计模式的ocp原则,即对扩展开放,对修改关闭。即当我们给类增加新功能的时候,尽量不修改代码,或者尽可能少修改代码.
  • 比如我们这时要新增加一个Pizza的种类(Pepper披萨),我们需要对orderPizza使用方进行修改

简单工厂模式

基本介绍

  • 简单工厂模式是属于创建型模式,是工厂模式的一种。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式
  • 简单工厂模式:定义了一个创建对象的类,由这个类来封装实例化对象的行为(代码)
  • 在软件开发中,当我们会用到大量的创建某种、某类或者某批对象时,就会使用到工厂模式

代码实现:

//pizza抽象类
public abstract class Pizza {
    protected String name;

    public void setName(String name) {
        this.name = name;
    }

    public abstract void prepare();

    public void bake(){
        System.out.println(name+"----烤");
    }
    public void cut(){
        System.out.println(name+"----切");
    }
    public void pack(){
        System.out.println(name+"----打包");
    }
}
//pizza实现类
public class GreekPizza extends Pizza{
    @Override
    public void prepare() {
        System.out.println("准备GreekPizza所需的材料");
    }
}
----
public class CheesePizza extends Pizza {
    @Override
    public void prepare() {
        System.out.println("准备CheesePizza所需的材料");
    }
}
//简单工厂类
public class SimplyFactory {
    public Pizza getPizza(String orderType){
        Pizza pizza=null;
        if(orderType.equals("g")){
            pizza=new GreekPizza();
            pizza.setName("夏威夷披萨");
        }else if(orderType.equals("c")){
            pizza=new CheesePizza();
            pizza.setName("芝士披萨");
        }
        return pizza;
    }
}
//orderPizza类
public class OrderPizza {
    SimplyFactory simplyFactory;
    Pizza pizza=null;
    public OrderPizza(SimplyFactory simplyFactory){
        setFactory(simplyFactory);
    }
    public void setFactory(SimplyFactory simplyFactory){
        this.simplyFactory=simplyFactory;
        String orderType="";
        do {
            orderType=getType();
            pizza=this.simplyFactory.getPizza(orderType);
            pizza.bake();
            pizza.cut();
            pizza.pack();
        }while (true);
    }

    public String getType() {
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("请输入你要预定的pizza:");
        try {
            String s = bufferedReader.readLine();
            return s;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

}

工厂方法模式

看一个新的需求:

披萨项目新的需求:客户在点披萨时,可以点不同口味的披萨,比如 北京的奶酪pizza、北京的胡椒pizza 或者是伦敦的奶酪pizza、伦敦的胡椒pizza。

基本介绍:

**工厂方法是粒度很小的设计模式,**因为模式的表现只是一个抽象的方法。提前定义用于创建对象的接口,让子类(具体工厂)决定实例化具体的某一个类,即在工厂和产品中间增加接口(抽象工厂),工厂不再负责产品的创建,由接口针对不同条件返回具体的类实例,由具体类实例(具体工厂)去实现。

代码实现:

//pizza抽象类
public abstract class Pizza {
    protected String name;

    public void setName(String name) {
        this.name = name;
    }

    public abstract void prepare();

    public void bake(){
        System.out.println(name+"----烤");
    }
    public void cut(){
        System.out.println(name+"----切");
    }
    public void pack(){
        System.out.println(name+"----打包");
    }
}
//LDGreek类
public class LDGreekPizza extends Pizza {

    @Override
    public void prepare() {
        System.out.println("准备GreekPizza所需的材料");
    }
}
//LDCheese类
public class LDCheesePizza extends Pizza {
    @Override
    public void prepare() {
        System.out.println("准备CheesePizza所需的材料");
    }
}
//CN的Greek和Cheese也如上所示
//orderPizza抽象类
public abstract class OrderPizza {

    //定义一个抽象方法,createPizza , 让各个工厂子类自己实现
    abstract Pizza createPizza(String orderType);

    // 构造器
    public OrderPizza() {
        Pizza pizza = null;
        String orderType; // 订购披萨的类型
        do {
            orderType = getType();
            pizza = createPizza(orderType); //抽象方法,由工厂子类完成
            //输出pizza 制作过程
            pizza.prepare();
            pizza.bake();
            pizza.cut();
            pizza.pack();

        } while (true);
    }

    // 写一个方法,可以获取客户希望订购的披萨种类
    private String getType() {
        try {
            BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("input pizza 种类:");
            String str = strin.readLine();
            return str;
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
    }
}
//orderPizza实现类
public class LDOrderPizza extends OrderPizza {
    @Override
    Pizza createPizza(String orderType) {
        Pizza pizza = null;
        if(orderType.equals("c")) {
            pizza = new LDCheesePizza();
        } else if (orderType.equals("g")) {
            pizza = new LDGreekPizza();
        }
        // TODO Auto-generated method stub
        return pizza;
    }
}
---
public class CNOrderPizza  extends OrderPizza{
    @Override
    Pizza createPizza(String orderType) {
        Pizza pizza = null;
        if(orderType.equals("c")) {
            pizza = new CNCheesePizza();
        } else if (orderType.equals("g")) {
            pizza = new CNGreekPizza();
        }
        // TODO Auto-generated method stub
        return pizza;
    }
}
    

抽象工厂模式

基本介绍

  • 抽象工厂模式:定义了一个interface用于创建相关或有依赖关系的对象簇,而无需指明具体的类
  • 抽象工厂模式可以将简单工厂模式工厂方法模式进行整合。
  • 从设计层面看,抽象工厂模式就是对简单工厂模式的改进(或者称为进一步的抽象)。
  • 将工厂抽象成两层,AbsFactory(抽象工厂) 和 具体实现的工厂子类。程序员可以根据创建对象类型使用对应的工厂子类。这样将单个的简单工厂类变成了工厂簇,更利于代码的维护和扩展。

代码实现:

//pizza抽象类
public abstract class Pizza {
    protected String name;

    public void setName(String name) {
        this.name = name;
    }

    public abstract void prepare();

    public void bake(){
        System.out.println(name+"----烤");
    }
    public void cut(){
        System.out.println(name+"----切");
    }
    public void pack(){
        System.out.println(name+"----打包");
    }
}
//LDGreek类
public class LDGreekPizza extends Pizza {

    @Override
    public void prepare() {
        System.out.println("准备GreekPizza所需的材料");
    }
}
//LDCheese类
public class LDCheesePizza extends Pizza {
    @Override
    public void prepare() {
        System.out.println("准备CheesePizza所需的材料");
    }
}
//CN的Greek和Cheese也如上所示

//抽象工厂
public  interface  AbsFactory {
    public abstract Pizza getPizza(String orderType);
}
//工厂实现类
public class CNFactory implements AbsFactory {
    @Override
    public Pizza getPizza(String orderType) {
        Pizza pizza=null;
        if(orderType.equals("g")){
            pizza=new CNGreekPizza();
            pizza.setName("CN夏威夷披萨");
        }else if(orderType.equals("c")){
            pizza=new CNCheesePizza();
            pizza.setName("CN芝士披萨");
        }
        return pizza;
    }
}
----
public class LDFactory implements AbsFactory {
    @Override
    public Pizza getPizza(String orderType) {
        Pizza pizza=null;
        if(orderType.equals("g")){
            pizza=new LDGreekPizza();
            pizza.setName("LD夏威夷披萨");
        }else if(orderType.equals("c")){
            pizza=new LDCheesePizza();
            pizza.setName("LD芝士披萨");
        }
        return pizza;
    }
}
//orderPizza
public class OrderPizza {
    AbsFactory absFactory;
    Pizza pizza=null;
    public OrderPizza(AbsFactory absFactory){
        setFactory(absFactory);
    }
    public void setFactory(AbsFactory absFactory){
        this.absFactory=absFactory;
        String orderType="";
        do {
            orderType=getType();
            pizza=this.absFactory.getPizza(orderType);
            if(pizza==null) {
                System.out.println("请输入正确的pizza名称");
                break;
            }
            pizza.prepare();
            pizza.bake();
            pizza.cut();
            pizza.pack();
        }while (true);
    }

    public String getType() {
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("请输入你要预定的pizza:");
        try {
            String s = bufferedReader.readLine();
            return s;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

}   

三种模式的特点

  • 简单工厂模式专门定义一个类来负责创建其他类的实例被创建的实例通常都具有共同的父类它又称为静态工厂方法模式。**它的实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类(这些产品类继承自一个父类或接口)的实例。**简单工厂模式的创建目标,所有创建的对象都是充当这个角色的某个具体类的实例。在这个模式中,工厂类是整个模式的关键所在。它包含必要的判断逻辑,能够根据外界给定的信息,决定究竟应该创建哪个具体类的对象。用户在使用时可以直接根据工厂类去创建所需的实例,而无需了解这些对象是如何创建以及如何组织的。有利于整个软件体系结构的优化。
  • 工厂方法模式:**工厂方法是粒度很小的设计模式,**因为模式的表现只是一个抽象的方法。提前定义用于创建对象的接口,让子类(具体工厂)决定实例化具体的某一个类,即在工厂和产品中间增加接口(抽象工厂),工厂不再负责产品的创建,由接口针对不同条件返回具体的类实例,由具体类实例(具体工厂)去实现。工厂方法模式是简单工厂模式的衍生,解决了许多简单工厂模式的问题。*首先完全实现ocp,实现了可扩展*。其次实现更复杂的层次结构,可以应用于产品结果复杂的场合。工厂方法模式是对简单工厂模式进行了抽象。有一个抽象的Factory类(可以是抽象类和接口),这个类将不在负责具体的产品生产,而是只制定一些规范,具体的生产工作由其子类去完成。在这个模式中,工厂类和产品类往往可以依次对应。即一个抽象工厂对应一个抽象产品,一个具体工厂对应一个具体产品,这个具体的工厂就负责生产对应的产品。
  • 抽象工厂模式:抽象工厂模式是所有形态的工厂模式中最为抽象和最具一般性的一种形态。抽象工厂模式是指当有多个抽象角色时,使用的一种工厂模式。抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定产品的具体的情况下,创建多个产品族中的产品对象。它有多个抽象产品类****,每个抽象产品类可以派生出多个具体产品类一个抽象工厂类,可以派生出多个具体工厂类每个具体工厂类可以创建多个具体产品类的实例。每一个模式都是针对一定问题的解决方案,工厂方法模式针对的是一个产品等级结构;**而抽象工厂模式针对的是多个产品等级结果。

三种模式的优点

  • 简单工厂模式:工厂类含有必要的判断逻辑**,可以决定在什么时候创建哪一个产品类的实例,**客户端可以免除直接创建产品对象的责任,而仅仅"消费"产品。简单工厂模式通过这种做法实现了对责任的分割。简单工厂模式能够根据外界给定的信息,决定究竟应该创建哪个具体类的对象。通过它,外界可以从直接创建具体产品对象的尴尬局面中摆脱出来。外界与具体类隔离开来,偶合性低。明确区分了各自的职责和权力,有利于整个软件体系结构的优化。
  • 工厂方法模式:**工厂方法模式是为了克服简单工厂模式的缺点(主要是为了满足OCP)而设计出来的。**简单工厂模式的工厂类随着产品类的增加需要增加很多方法(或代码),而工厂方法模式每个具体工厂类只完成单一任务,代码简洁。工厂方法模式完全满足OCP,即它有非常良好的扩展性。
  • 抽象工厂模式:抽象工厂模式主要在于应对“新系列”的需求变化。分离了具体的类,抽象工厂模式帮助你控制一个应用创建的对象的类,因为一个工厂封装创建产品对象的责任和过程。它将客户和类的实现分离,客户通过他们的抽象接口操纵实例,产品的类名也在具体工厂的实现中被分离,它们不出现在客户代码中。它使得易于交换产品系列。一个具体工厂类在一个应用中仅出现一次——即在它初始化的时候。这使得改变一个应用的具体工厂变得很容易。它只需改变具体的工厂即可使用不同的产品配置,**这是因为一个抽象工厂创建了一个完整的产品系列,**所以整个产品系列会立刻改变。它有利于产品的一致性。当一个系列的产品对象被设计成一起工作时,一个应用一次只能使用同一个系列中的对象,这一点很重要,而抽象工厂很容易实现这一点。抽象工厂模式有助于这样的团队的分工,降低了模块间的耦合性,提高了团队开发效率。

三种模式的缺点

  • **简单工厂模式:**当产品有复杂的多层等级结构时,工厂类只有自己,以不变应万变,就是模式的缺点。因为工厂类集中了所有产品创建逻辑,一旦增加产品或者删除产品,整个系统都要受到影响。系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,有可能造成工厂逻辑过于复杂,违背了"开放–封闭"原则(OCP).另外,简单工厂模式通常使用静态工厂方法,这使得无法由子类继承,造成工厂角色无法形成基于继承的等级结构。
  • 工厂方法模式:假如某个具体产品类需要进行一定的修改,很可能需要修改对应的工厂类。当同时需要修改多个产品类的时候,对工厂类的修改会变得相当麻烦。比如说,每增加一个产品,相应的也要增加一个子工厂,会加大了额外的开发量。
  • 抽象工厂模式:抽象工厂模式在于难于应付新对象”的需求变动。难以支持新种类的产品。难以扩展抽象工厂以生产新种类的产品。这是因为抽象工厂几乎确定了可以被创建的产品集合,支持新种类的产品就需要扩展该工厂接口,这将涉及抽象工厂类及其所有子类的改变。

三种模式使用场景

  • 简单工厂模式:工厂类负责创建的对象比较少,客户只知道传入了工厂类的参数,对于始何创建对象(逻辑)不关心。
  • 工厂方法模式:当一个类不知道它所必须创建对象的类或一个类希望由子类来指定它所创建的对象时当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候,可以使用工厂方法,支持多扩展少修改的OCP原则。
  • 抽象工厂模式:一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有形态的工厂模式都是重要的。这个系统有多于一个的产品族而系统只消费其中某一产品族。同属于同一个产品族的产品是在一起使用的,这一约束必须在系统的设计中体现出来。系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于实现。
  • 其实,无论是简单工厂模式**、工厂模式还是抽象工厂模式,它们本质上都是将不变的部分提取出来,将可变的部分留作接口,以达到最大程度上的复用。**究竟用哪种设计模式更适合,这要根据具体的业务需求来决定。

工厂模式在JDK-Calendar 应用的

JDK 中的Calendar类中,就使用了简单工厂模式

原型模式

基本介绍

  • 原型模式(Prototype模式)是指:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象
  • 原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象, 无需知道如何创建的细节
  • 工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建,即 对象.clone()

案例理解

克隆羊问题现在有一只羊tom,姓名为: tom, 年龄为:1,颜色为:白色,请编写程序创建和tom羊 属性完全相同的10只羊。

传统代码模式

代码实现:

//sheep类
public class Sheep {

    private String name;
    private int age;

    public Sheep() {
    }

    public Sheep(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Sheep{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
//client
public class Client {
    public static void main(String[] args) {
        Sheep sheep=new Sheep("阿杰",1);//原型
        Sheep sheep1=new Sheep(sheep.getName(),sheep.getAge());
        Sheep sheep2=new Sheep(sheep.getName(),sheep.getAge());
        Sheep sheep3=new Sheep(sheep.getName(),sheep.getAge());
        Sheep sheep4=new Sheep(sheep.getName(),sheep.getAge());
        Sheep sheep5=new Sheep(sheep.getName(),sheep.getAge());

        System.out.println(sheep.hashCode());
        System.out.println(sheep1.hashCode());
        System.out.println(sheep2.hashCode());
        System.out.println(sheep3.hashCode());
        System.out.println(sheep4.hashCode());
        System.out.println(sheep5.hashCode());

    }
}

优缺点:

  • 优点是比较好理解,简单易操作。
  • 在创建新的对象时,总是需要重新获取原始对象的属性,如果创建的对象比较复杂时,效率较低
  • 总是需要重新初始化对象,而不是动态地获得对象运行时的状态, 不够灵活

改进的思路分析:

**思路:**Java中Object类是所有类的根类,Object类提供了一个clone()方法,该方法可以将一个Java对象复制一份,但是需要实现clone的Java类必须要实现一个接口Cloneable,该接口表示该类能够复制且具有复制的能力 => 原型模式

原型模式

浅拷贝

浅拷贝的介绍

  • 对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。
  • 对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值
  • 浅拷贝是使用默认的 clone()方法来实现sheep = (Sheep) super.clone();

代码实现:

//具体原型实现原型接口
public class Sheep implements Cloneable{

    private String name;
    private int age;

    public Sheep() {
    }

    public Sheep(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Sheep{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Sheep sheep= (Sheep) super.clone();
        return sheep;
    }
}
//client
public class client {                                                                   
    public static void main(String[] args) throws CloneNotSupportedException {          
        Sheep sheep=new Sheep("杰",0);                                                   
        Sheep sheep1= (Sheep) sheep.clone();                                            
        Sheep sheep2= (Sheep) sheep.clone();                                            
        Sheep sheep3= (Sheep) sheep.clone();                                            
        Sheep sheep4= (Sheep) sheep.clone();                                            
        Sheep sheep5= (Sheep) sheep.clone();                                                                                                               
        System.out.println(sheep+"-----------"+sheep.hashCode());                       
        System.out.println(sheep1+"-----------"+sheep.hashCode());                      
        System.out.println(sheep2+"-----------"+sheep.hashCode());                      
        System.out.println(sheep3+"-----------"+sheep.hashCode());                      
        System.out.println(sheep4+"-----------"+sheep.hashCode());                      
        System.out.println(sheep5+"-----------"+sheep.hashCode());               }                                                                            
深拷贝

深拷贝基本介绍

  • 复制对象的所有基本数据类型的成员变量值
  • 为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象进行拷贝
  • 深拷贝实现方式1:重写clone方法来实现深拷贝
  • 深拷贝实现方式2:通过对象序列化实现深拷贝(推荐)

代码实现:

//wool羊毛类,使用默认重写
public class Wool implements Cloneable, Serializable {
    private String color;

    public Wool() {
    }

    public Wool(String color) {
        this.color = color;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    @Override
    public String toString() {
        return "Wool{" +
                "color='" + color + '\'' +
                '}';
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Wool wool= (Wool) super.clone();
        return wool;
    }
}
//羊类,两种深拷贝方式
public class Sheep implements  Cloneable, Serializable {

    private String name;
    private int age;
    private Wool wool;

    public Sheep() {
    }

    public Sheep(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Wool getWool() {
        return wool;
    }

    public void setWool(Wool wool) {
        this.wool = wool;
    }

    @Override
    public String toString() {
        return "Sheep{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", wool=" + wool +
                '}';
    }
	//重写clone方法来实现深拷贝
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Sheep sheep=null;
        sheep= (Sheep) super.clone();
        sheep.setWool((Wool) wool.clone());
        return sheep;

    }
	//通过对象序列化实现深拷贝(推荐)
    protected Object deepClone() {
        //创建流对象
        ByteArrayOutputStream bos = null;
        ObjectOutputStream oos = null;
        ByteArrayInputStream bis = null;
        ObjectInputStream ois = null;

        try {
            bos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bos);
            oos.writeObject(this);
            bis = new ByteArrayInputStream(bos.toByteArray());
            ois = new ObjectInputStream(bis);
            Sheep sheep = (Sheep) ois.readObject();
            return sheep;

        } catch (Exception e) {
           return  e.getMessage();
        } finally {
            try {
                bos.close();
                bis.close();
                oos.close();
                ois.close();
            } catch (Exception e) {
                return e.getMessage();
            }
        }
    }
}
//Client
public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        Sheep sheep=new Sheep("羊",1);
        sheep.setWool(new Wool("red"));
        Sheep sheep1= (Sheep) sheep.clone();
        Sheep sheep2= (Sheep) sheep.clone();
        Sheep sheep3= (Sheep) sheep.clone();
        Sheep sheep4= (Sheep) sheep.clone();
        Sheep sheep5= (Sheep) sheep.clone();

        System.out.println(sheep+"----"+sheep.hashCode()+"-------"+sheep.getWool()+"---------"+sheep.getWool().hashCode());
        System.out.println(sheep1+"----"+sheep1.hashCode()+"-------"+sheep1.getWool()+"---------"+sheep1.getWool().hashCode());
        System.out.println(sheep2+"----"+sheep2.hashCode()+"-------"+sheep2.getWool()+"---------"+sheep2.getWool().hashCode());
        System.out.println(sheep3+"----"+sheep3.hashCode()+"-------"+sheep3.getWool()+"---------"+sheep3.getWool().hashCode());
        System.out.println(sheep4+"----"+sheep4.hashCode()+"-------"+sheep4.getWool()+"---------"+sheep4.getWool().hashCode());
        System.out.println(sheep5+"----"+sheep5.hashCode()+"-------"+sheep5.getWool()+"---------"+sheep5.getWool().hashCode());

        System.out.println("--------------------------------------------------------------------------------------------------");

        Sheep deepSheep=new Sheep("深羊",6);
        deepSheep.setWool(new Wool("blue"));
        Sheep deepSheep1= (Sheep) deepSheep.deepClone();
        Sheep deepSheep2= (Sheep) deepSheep.deepClone();
        Sheep deepSheep3= (Sheep) deepSheep.deepClone();
        Sheep deepSheep4= (Sheep) deepSheep.deepClone();
        Sheep deepSheep5= (Sheep) deepSheep.deepClone();


        System.out.println(deepSheep+"----"+deepSheep.hashCode()+"-------"+deepSheep.getWool()+"---------"+deepSheep.getWool().hashCode());
        System.out.println(deepSheep1+"----"+deepSheep1.hashCode()+"-------"+deepSheep1.getWool()+"---------"+deepSheep1.getWool().hashCode());
        System.out.println(deepSheep2+"----"+deepSheep2.hashCode()+"-------"+deepSheep2.getWool()+"---------"+deepSheep2.getWool().hashCode());
        System.out.println(deepSheep3+"----"+deepSheep3.hashCode()+"-------"+deepSheep3.getWool()+"---------"+deepSheep3.getWool().hashCode());
        System.out.println(deepSheep4+"----"+deepSheep4.hashCode()+"-------"+deepSheep4.getWool()+"---------"+deepSheep4.getWool().hashCode());
        System.out.println(deepSheep5+"----"+deepSheep5.hashCode()+"-------"+deepSheep5.getWool()+"---------"+deepSheep5.getWool().hashCode());
}

优缺点:

  • 创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率
  • 不用重新初始化对象,而是动态地获得对象运行时的状态
  • 如果原始对象发生变化(增加或者减少属性),其它克隆对象的也会发生相应的变化,无需修改代码
  • 在实现深克隆的时候可能需要比较复杂的代码
  • 缺点:需要为每一个类配备一个克隆方法,这对全新的类来说不是很难,但对已有的类进行改造时,需要修改其源代码,违背了ocp原则,这点请同学们注意.

原型模式在Spring框架中源码的应用

Spring中原型bean的创建,就是原型模式的应用

建造者模式

基本介绍

建造者模式(Builder Pattern) 又叫生成器模式,是一种对象构建模式。它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。

建造者模式 是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。

建造者模式的四个角色

**Product(产品角色): **一个具体的产品对象。

Builder(抽象建造者): 创建一个Product对象的各个部件指定的 接口/抽象类

ConcreteBuilder(具体建造者): 实现接口,构建和装配各个部件。

Director(指挥者): 构建一个使用Builder接口的对象。它主要是用于创建一个复杂的对象。它主要有两个作用,一是:隔离了客户与对象的生产过程,二是:负责控制产品对象的生产过程。

案例理解

盖房项目需求:

  • 需要建房子:这一过程为打桩、砌墙、封顶
  • 房子有各种各样的,比如普通房,高楼,别墅,各种房子的过程虽然一样,但是要求不要相同的.

传统代码模式

代码实现:

//造房子抽象类
public abstract class AbsBuilderHouse {
    public abstract void  pile();
    public abstract void  wall();
    public abstract void  roof();

    public void builder(){
        pile();
        wall();
        roof();
    }
}
//造房子实现类
public class CommonHouse extends AbsBuilderHouse {
    @Override
    public void pile() {
        System.out.println("打地基");
    }

    @Override
    public void wall() {
        System.out.println("砌墙");
    }

    @Override
    public void roof() {
        System.out.println("盖屋顶");
    }
}
//client
public class Client {
    public static void main(String[] args) {
        CommonHouse commonHouse=new CommonHouse();
        commonHouse.builder();
    }
}

优缺点:

  • 优点是比较好理解,简单易操作。
  • 设计的程序结构,过于简单,没有设计缓存层对象,程序的扩展和维护不好. 也就是说,这种设计方案,把产品(即:房子) 和 创建产品的过程(即:建房子流程) 封装在一起,耦合性增强了

建造者模式

代码实现:

//产品类
public class House {
    private String pile;
    private String wall;
    private String roof;

    public String getPile() {
        return pile;
    }

    public void setPile(String pile) {
        this.pile = pile;
    }

    public String getWall() {
        return wall;
    }

    public void setWall(String wall) {
        this.wall = wall;
    }

    public String getRoof() {
        return roof;
    }

    public void setRoof(String roof) {
        this.roof = roof;
    }

    @Override
    public String toString() {
        return "House{" +
                "pile='" + pile + '\'' +
                ", wall='" + wall + '\'' +
                ", roof='" + roof + '\'' +
                '}';
    }
}
//抽象建造者类
public abstract class HouseBuilder {
    protected House house=new House();

    public abstract void buildPile();
    public abstract void buildWall();
    public abstract void buildRoof();

    public House getHouse(){
        return house;
    }
}
//具体建造者类
public class CommonHouse extends HouseBuilder {
    @Override
    public void buildPile() {
        house.setPile("打地基10米");
        System.out.println(house.getPile());

    }

    @Override
    public void buildWall() {
        house.setWall("贴砖20米");
        System.out.println(house.getWall());
    }

    @Override
    public void buildRoof() {
        house.setRoof("盖普通房顶");
        System.out.println(house.getRoof());
    }
}
----
public class HighHouse extends  HouseBuilder{

    @Override
    public void buildPile() {
        System.out.println("房子打地基5米");
    }

    @Override
    public void buildWall() {
        System.out.println("房子建墙10米");
    }

    @Override
    public void buildRoof() {
        System.out.println("房子盖豪华房顶");
    }
}
//指挥者类
public class Director {

    private HouseBuilder houseBuilder;

    public Director(HouseBuilder houseBuilder) {
        this.houseBuilder = houseBuilder;
    }

    public void setHouseBuilder(HouseBuilder houseBuilder) {
        this.houseBuilder = houseBuilder;
    }

    public House constructHouse(){
        houseBuilder.buildPile();
        houseBuilder.buildWall();
        houseBuilder.buildRoof();
        return houseBuilder.getHouse();
    }
}
//主方法类
public class Client {
    public static void main(String[] args) {

        CommonHouse commonHouse=new CommonHouse();
        Director director=new Director(commonHouse);
        House house=director.constructHouse();
        System.out.println(house);

    }
}

t.println(house.getPile());

}

@Override
public void buildWall() {
    house.setWall("贴砖20米");
    System.out.println(house.getWall());
}

@Override
public void buildRoof() {
    house.setRoof("盖普通房顶");
    System.out.println(house.getRoof());
}

}

public class HighHouse extends HouseBuilder{

@Override
public void buildPile() {
    System.out.println("房子打地基5米");
}

@Override
public void buildWall() {
    System.out.println("房子建墙10米");
}

@Override
public void buildRoof() {
    System.out.println("房子盖豪华房顶");
}

}
//指挥者类
public class Director {

private HouseBuilder houseBuilder;

public Director(HouseBuilder houseBuilder) {
    this.houseBuilder = houseBuilder;
}

public void setHouseBuilder(HouseBuilder houseBuilder) {
    this.houseBuilder = houseBuilder;
}

public House constructHouse(){
    houseBuilder.buildPile();
    houseBuilder.buildWall();
    houseBuilder.buildRoof();
    return houseBuilder.getHouse();
}

}
//主方法类
public class Client {
public static void main(String[] args) {

    CommonHouse commonHouse=new CommonHouse();
    Director director=new Director(commonHouse);
    House house=director.constructHouse();
    System.out.println(house);

}

}

参考资料地址说明

参考资料来源:www.atguigu.com

[参考资料来源][www.atguigu.com]

如若侵权,请联系删除

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值