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

单例模式与工厂模式

作者: 攻城老狮 | 来源:发表于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