美文网首页
单例模式与工厂模式

单例模式与工厂模式

作者: 攻城老狮 | 来源:发表于2020-06-27 11:37 被阅读0次

单例模式与工厂模式

参考教程:https://www.bilibili.com/video/BV1G4411c7N4
代码实现 Github:https://github.com/yaokuku123/pattern


概述

设计模式分为三大类型,共23种

  • 创建者模式:单例模式,工厂模式,抽象工厂模式,原型模式,建造者模式
  • 结构型模式:适配器模式,桥接模式,装饰模式,组合模式,外观模式,享元模式,代理模式
  • 行为型模式:模板方法模式,命令模式,访问者模式,迭代器模式,观察者模式,中介者模式,备忘录模式,解释器模式,状态模式,策略模式,责任链模式

单例模式

  1. 单例模式的解释与使用场景
  • 解释:采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)。
  • 使用场景:需要频繁的进行创建和销毁的对象、创建对象时耗时过多或 耗费资源过多(即:重量级对象),但又经常用到的对象、工具类对象、频繁访问数 据库或文件的对象(比如数据源、session工厂等)
  1. 实现方式
  • 饿汉式
class Singleton{
    //final可以提升一定效率
    //类加载时赋值,线程安全
    private static final Singleton instance = new Singleton();

    private Singleton() {}

    public static Singleton getInstance(){
        return instance;
    }
}
  • 饿汉式(静态代码块)
class Singleton {
    private static Singleton instance;
    //静态代码块,在类加载时执行,线程安全
    static {
        instance = new Singleton();
    }

    private Singleton() {}

    public static Singleton getInstance(){
        return instance;
    }
}
  • 懒汉式(线程安全但效率低)
class Singleton {
    private static Singleton instance;

    private Singleton() {}
    //互斥访问共享资源,线程安全。但由于每次获取均需加锁,效率低
    public static synchronized Singleton getInstance() {
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}
  • 懒汉式(线程安全且效率高,DoubleCheck)
class Singleton{
    //volatile防止指令重排
    private static volatile Singleton instance;

    private Singleton() {}
    //doubleCheck,规避了效率低且满足线程安全
    public static Singleton getInstance(){
        if(instance == null){
            synchronized (Singleton.class){
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
  • 静态内部类的方式创建
class Singleton{
    private static Singleton instance;

    private Singleton() {}
    //定义静态内部类,调用内部类创建实例,只有当调用静态内部类时,该内部类才会被创建,从而实现懒加载且线程安全。
    private static class SingletonInstance{
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance(){
        return SingletonInstance.INSTANCE;
    }
}
  • 枚举的方式创建(推荐)
public class Singleton06 {
    public static void main(String[] args) {
        Singleton instance = Singleton.INSTANCE;
        Singleton instance2 = Singleton.INSTANCE;
        System.out.println(instance == instance2);
        instance.doSomething();
    }
}

enum Singleton{
    INSTANCE;
    //在枚举类中也可以定义方法
    public void doSomething(){
        System.out.println("Hello World");
    }
}

工厂模式

1. 简单工厂模式

  1. 案例描述

制作披萨的项目:要便于披萨种类的扩展,要便于维护

  • 披萨的种类很多(比如 GreekPizza、CheesePizza 等)
  • 披萨的制作有 prepare,bake, cut, box
  • 完成披萨店订购功能
  1. 传统实现方案
3.png
  • Pizza类
package com.yqj.pattern.factory.simpleFactory.triditional.pizza;

public abstract class Pizza {
    //对子类可见,对其他类透明
    protected String pizzaName;

    public void setPizzaName(String pizzaName) {
        this.pizzaName = pizzaName;
    }
    //由子类实现
    public abstract void prepare();

    public void bake() {
        System.out.println(pizzaName + "烘焙披萨");
    }

    public void cut() {
        System.out.println(pizzaName + "切披萨");
    }

    public void box() {
        System.out.println(pizzaName + "打包披萨");
    }
}
  • Pizza的子类
// CheesePizza类
package com.yqj.pattern.factory.simpleFactory.triditional.pizza;

public class CheesePizza extends Pizza {
    @Override
    public void prepare() {
        setPizzaName("希腊pizza");
        System.out.println(pizzaName+"准备制作");
    }
}

// GreekPizza类
package com.yqj.pattern.factory.simpleFactory.triditional.pizza;

public class GreekPizza extends Pizza {

    @Override
    public void prepare() {
        setPizzaName("奶酪pizza");
        System.out.println(pizzaName + "准备制作");
    }
}
  • OrderPizza类,用于定制pizza
package com.yqj.pattern.factory.simpleFactory.triditional.order;

import com.yqj.pattern.factory.simpleFactory.triditional.pizza.CheesePizza;
import com.yqj.pattern.factory.simpleFactory.triditional.pizza.GreekPizza;
import com.yqj.pattern.factory.simpleFactory.triditional.pizza.Pizza;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class OrderPizza {
    Pizza pizza = null;
    String orderType = null;
    //完成定制pizza
    public OrderPizza(){
        do {
            orderType = getOrderType();
            if("cheese".equals(orderType)){
                pizza = new CheesePizza();
            } else if ("greek".equals(orderType)){
                pizza = new GreekPizza();
            } else {
                break;
            }
            pizza.prepare();
            pizza.bake();
            pizza.cut();
            pizza.box();
        } while (true);
    }
    //从终端获取数值
    private String getOrderType(){
        try {
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("input pizza type: ");
            String orderType = bufferedReader.readLine();
            return orderType;
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
    }

    public static void main(String[] args) {
        new OrderPizza();
    }
}
  • 分析

传统的方式虽然实现了功能,但违背了开闭原则(ocp)。需要扩展功能增加新品类pizza时,会导致调用者代码发生修改。若调用者已创建多个,则当扩展功能时每个调用者的代码均需要修改。不利于软件的扩展性。代码如下:

若此时扩展新品类PepperPizza

// PepperPizza类
package com.yqj.pattern.factory.simpleFactory.triditional.pizza;

public class PepperPizza extends Pizza {

    @Override
    public void prepare() {
        setPizzaName("胡椒pizza");
        System.out.println(pizzaName + "准备制作");
    }
}

则调用者中的代码也需要修改,新增判断PepperPizza的代码片段

//完成定制pizza
    public OrderPizza(){
        do {
            orderType = getOrderType();
            if("cheese".equals(orderType)){
                pizza = new CheesePizza();
            } else if ("greek".equals(orderType)){
                pizza = new GreekPizza();
             //修改部分 ************************************************** 
            } else if("pepper".equals(orderType)){
                pizza = new PepperPizza();
            } else {
                break;
            }
            pizza.prepare();
            pizza.bake();
            pizza.cut();
            pizza.box();
        } while (true);
    }
  1. 简单工厂模式的解释与使用场景
  • 解释:简单工厂模式是属于创建型模式,是工厂模式的一种。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例,由这个类来封装实例化对象的行为(代码)。
  • 使用场景:当我们会用到大量的创建某种、某类或者某批对象时,就会使用到工厂模式。
  1. 改进后的代码
2.png
  • Pizza类不变
  • SimpleFactory工厂类
package com.yqj.pattern.factory.simpleFactory.improve.order;

import com.yqj.pattern.factory.simpleFactory.improve.pizza.CheesePizza;
import com.yqj.pattern.factory.simpleFactory.improve.pizza.GreekPizza;
import com.yqj.pattern.factory.simpleFactory.improve.pizza.Pizza;
//工厂类用于创建实例对象
public class SimpleFactory {
    //工厂类负责根据不同的传入参数创建出不同的产品,当需要扩展时,仅在本工厂类中做修改即可,使调用者无需做代码的修改。
    public Pizza getFactory(String orderType){
        Pizza pizza = null;
        if("cheese".equals(orderType)){
            pizza = new CheesePizza();
        } else if ("greek".equals(orderType)){
            pizza = new GreekPizza();
        }
        return pizza;
    }
}
  • OrderPizza类
package com.yqj.pattern.factory.simpleFactory.improve.order;

import com.yqj.pattern.factory.simpleFactory.improve.pizza.CheesePizza;
import com.yqj.pattern.factory.simpleFactory.improve.pizza.GreekPizza;
import com.yqj.pattern.factory.simpleFactory.improve.pizza.Pizza;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class OrderPizza {
    Pizza pizza = null;
    SimpleFactory factory = null;

    public OrderPizza(SimpleFactory factory){
        setFactory(factory);
    }

    public void setFactory(SimpleFactory factory){
        this.factory = factory;
        String orderType;
        do{
            orderType = getOrderType();
            //使用工厂来获取pizza
            pizza = this.factory.getFactory(orderType);
            //通过工厂模式创建的实例,完成后续的方法调用
            if(pizza != null){
                pizza.prepare();
                pizza.bake();
                pizza.cut();
                pizza.box();
            }else {
                System.out.println("订购披萨失败");
                break;
            }
        }while (true);
    }

    private String getOrderType(){
        try {
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("input pizza type: ");
            String orderType = bufferedReader.readLine();
            return orderType;
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
    }

    public static void main(String[] args) {
        new OrderPizza(new SimpleFactory());
    }
}
  • 分析

使用简单工厂模式,实现了开闭原则。当需要增加新的品类时,不需要修改调用者中的代码,而仅通过修改工厂类中的实例代码即可。实现了较强的扩展性。

  1. 改进代码(使用静态工厂的方式),进一步简化调用者的代码,无需在类中保存工厂的实例对象。但灵活性降低
  • Pizza类不变
  • SimpleStaticFactory类 (不变,仅修改类的名字为SimpleStaticFactory)
package com.yqj.pattern.factory.simpleFactory.staticimprove.order;

import com.yqj.pattern.factory.simpleFactory.staticimprove.pizza.CheesePizza;
import com.yqj.pattern.factory.simpleFactory.staticimprove.pizza.GreekPizza;
import com.yqj.pattern.factory.simpleFactory.staticimprove.pizza.Pizza;


public class SimpleStaticFactory {
    public static Pizza getFactory(String orderType){
        Pizza pizza = null;
        if("cheese".equals(orderType)){
            pizza = new CheesePizza();
        } else if ("greek".equals(orderType)){
            pizza = new GreekPizza();
        }
        return pizza;
    }
}
  • OrderFactory类
package com.yqj.pattern.factory.simpleFactory.staticimprove.order;

import com.yqj.pattern.factory.simpleFactory.staticimprove.pizza.Pizza;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class OrderPizza {
    Pizza pizza = null;

    public OrderPizza(){
        setFactory();
    }

    public void setFactory(){
        String orderType;
        do{
            orderType = getOrderType();
            //使用静态工厂的方式创建实例
            pizza = SimpleStaticFactory.getFactory(orderType);
            if(pizza != null){
                pizza.prepare();
                pizza.bake();
                pizza.cut();
                pizza.box();
            }else {
                System.out.println("订购披萨失败");
                break;
            }
        }while (true);
    }

    private String getOrderType(){
        try {
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("input pizza type: ");
            String orderType = bufferedReader.readLine();
            return orderType;
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
    }

    public static void main(String[] args) {
        new OrderPizza();
    }
}

2. 工厂方法模式

  1. 案例需求

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

  1. 传统方式

使用简单工厂模式,创建不同的简单工厂类,比如:BJPizzaSimpleFactory,LDPizzaFactory等,但若新增加新地点的例如上海pizza时,导致ocp问题。降低软件的可维护性和可扩展性。

  1. 工厂方法模式

解释:定义一个创建对象的抽象方法,由子类决定要实例化的类,工厂方法模式将对象的实例化推迟到子类中。

  1. 改进代码
1.png
  • pizza类
//pizza类
package com.yqj.pattern.factory.factorymethod.pizza;

public abstract class Pizza {
    protected String pizzaName;

    public void setPizzaName(String pizzaName) {
        this.pizzaName = pizzaName;
    }

    public abstract void prepare();

    public void bake() {
        System.out.println(pizzaName + "烘焙披萨");
    }

    public void cut() {
        System.out.println(pizzaName + "切披萨");
    }

    public void box() {
        System.out.println(pizzaName + "打包披萨");
    }
}

//BJGreekPizza类
package com.yqj.pattern.factory.factorymethod.pizza;

public class BJGreekPizza extends Pizza {

    @Override
    public void prepare() {
        setPizzaName("北京希腊pizza");
        System.out.println(pizzaName + "准备制作");
    }
}

//BJCheesePizza类
package com.yqj.pattern.factory.factorymethod.pizza;

public class BJCheesePizza extends Pizza {
    @Override
    public void prepare() {
        setPizzaName("北京奶酪pizza");
        System.out.println(pizzaName+"准备制作");
    }
}

//LDGreekPizza类
package com.yqj.pattern.factory.factorymethod.pizza;

public class LDGreekPizza extends Pizza {
    @Override
    public void prepare() {
        setPizzaName("伦敦希腊pizza");
        System.out.println(pizzaName + "准备制作");
    }
}

//LDCheesePizza类
package com.yqj.pattern.factory.factorymethod.pizza;

public class LDCheesePizza extends Pizza {

    @Override
    public void prepare() {
        setPizzaName("伦敦奶酪pizza");
        System.out.println(pizzaName + "准备制作");
    }
}
  • order类
package com.yqj.pattern.factory.factorymethod.order;

import com.yqj.pattern.factory.factorymethod.pizza.*;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

class BJOrderPizza extends OrderPizza{

    @Override
    public Pizza createPizza(String orderType) {
        Pizza pizza = null;
        if("cheese".equals(orderType)){
            pizza = new BJCheesePizza();
        } else if("greek".equals(orderType)){
            pizza = new BJGreekPizza();
        }
        return pizza;
    }
}

class LDOrderPizza extends OrderPizza{

    @Override
    public Pizza createPizza(String orderType) {
        Pizza pizza = null;
        if("cheese".equals(orderType)){
            pizza = new LDCheesePizza();
        }else if("greek".equals(orderType)){
            pizza = new LDGreekPizza();
        }
        return pizza;
    }
}

public abstract class OrderPizza {

    public abstract Pizza createPizza(String orderType);

    public OrderPizza(){
        Pizza pizza = null;
        do{
            String orderType = getOrderType();
            pizza = createPizza(orderType);
            if(pizza!=null){
                pizza.prepare();
                pizza.bake();
                pizza.cut();
                pizza.box();
            } else {
                System.out.println("订购失败");
                break;
            }

        }while (true);
    }


    private String getOrderType(){
        try {
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("input pizza type: ");
            String orderType = bufferedReader.readLine();
            return orderType;
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
    }

    public static void main(String[] args) {
        new BJOrderPizza();
    }
}

抽象工厂模式

  1. 抽象工厂模式

解释:定义了一个interface用于创建相关或有依赖关系的对象簇,而无需指明具体的类。抽象工厂模式可以将简单工厂模式和工厂方法模式进行整合。程序员可以 根据创建对象类型使用对应的工厂子类。这样将单个的简单工厂类变成了工厂簇, 更利于代码的维护和扩展。

  1. 改进代码
3-1592180596412.png
  • Pizza类与上面工厂模式的一致

  • Factory类

//AbsFactory类
package com.yqj.pattern.factory.abstractfactory.order;

import com.yqj.pattern.factory.abstractfactory.pizza.Pizza;

public interface AbsFactory {
    Pizza createPizza(String orderType);
}

//BJFactory类
package com.yqj.pattern.factory.abstractfactory.order;

import com.yqj.pattern.factory.abstractfactory.pizza.BJCheesePizza;
import com.yqj.pattern.factory.abstractfactory.pizza.BJGreekPizza;
import com.yqj.pattern.factory.abstractfactory.pizza.Pizza;

public class BJFactory implements AbsFactory {
    @Override
    public Pizza createPizza(String orderType) {
        Pizza pizza = null;
        if("cheese".equals(orderType)){
            pizza = new BJCheesePizza();
        }else if("greek".equals(orderType)){
            pizza = new BJGreekPizza();
        }
        return pizza;
    }
}
//LDFactory类
package com.yqj.pattern.factory.abstractfactory.order;


import com.yqj.pattern.factory.abstractfactory.pizza.LDCheesePizza;
import com.yqj.pattern.factory.abstractfactory.pizza.LDGreekPizza;
import com.yqj.pattern.factory.abstractfactory.pizza.Pizza;

public class LDFactory implements AbsFactory {
    @Override
    public Pizza createPizza(String orderType) {
        Pizza pizza = null;
        if("cheese".equals(orderType)){
            pizza = new LDCheesePizza();
        }else if("greek".equals(orderType)){
            pizza = new LDGreekPizza();
        }
        return pizza;
    }
}
  • OrderPizza类
package com.yqj.pattern.factory.abstractfactory.order;


import com.yqj.pattern.factory.abstractfactory.pizza.Pizza;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class OrderPizza {
    AbsFactory factory;

    public OrderPizza(AbsFactory factory){
        setFactory(factory);
    }

    public void setFactory(AbsFactory factory){
        this.factory = factory;
        Pizza pizza = null;
        do{
            String orderType = getOrderType();
            pizza = this.factory.createPizza(orderType);
            if(pizza!=null){
                pizza.prepare();
                pizza.bake();
                pizza.cut();
                pizza.box();
            }else {
                System.out.println("订购失败");
                break;
            }
        }while (true);
    }

    private String getOrderType(){
        try {
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("input pizza type: ");
            String orderType = bufferedReader.readLine();
            return orderType;
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
    }

    public static void main(String[] args) {
        new OrderPizza(new BJFactory());
    }
}

工厂模式小结

  • 工厂模式的意义 将实例化对象的代码提取出来,放到一个类中统一管理和维护,达到和主项目的 依赖关系的解耦。从而提高项目的扩展和维护性。
  • 三种工厂模式 (简单工厂模式、工厂方法模式、抽象工厂模式)
  • 设计模式的依赖抽象原则

相关文章

网友评论

      本文标题:单例模式与工厂模式

      本文链接:https://www.haomeiwen.com/subject/ckmmfktx.html