zoukankan      html  css  js  c++  java
  • Java设计模式(5:设计模式的分类及工厂模式详解)

    一、设计模式的分类

    总的来说,设计模式可以分为三大类:创建型模式结构型模式行为型模式,具体如下图:

    设计模式.png

    二、工厂模式

    工厂模式分为简单工厂模式工厂方法模式抽象工厂模式。其中简单工厂模式并不属于23种设计模式,但并不影响它的广泛使用。在JDK的源码当中,就存在着许多这样的例子。

    2.1 简单工厂模式

    我们先来看一段代码:

    public static void main(String[] args) {
        // 日历类
        Calendar calendar = Calendar.getInstance();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("当前时间为:" + simpleDateFormat.format(calendar.getTime()));
        calendar.add(Calendar.HOUR,2);
        System.out.println("当前时间加了两个小时后,时间是: " + simpleDateFormat.format(calendar.getTime()));
    }
    

    这段代码,大家应该比较熟悉,通过对Calendar的一系列操作,打印出当前时间和当前时间加两个小时后的时间,这里我们来看看结果:

    image20210618100232505.png

    结果正和我们想象的一样,两次打印出来的时间相隔两个小时。但我们今天的重点是Calendar calendar = Calendar.getInstance()这段代码,通过getInstance()方法拿到了Calendar 类的实例。来看看具体的源代码:

    public static Calendar getInstance(){
            return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT));
      }
    
    //  代码不全,有兴趣的朋友可以去看JDK源码
     private static Calendar createCalendar(TimeZone zone, Locale aLocale){
            // 中间的代码省略.....
            Calendar cal = null;
            if (aLocale.hasExtensions()) {
                String caltype = aLocale.getUnicodeLocaleType("ca");
                if (caltype != null) {
                    switch (caltype) {
                    case "buddhist":
                    cal = new BuddhistCalendar(zone, aLocale);
                        break;
                    case "japanese":
                        cal = new JapaneseImperialCalendar(zone, aLocale);
                        break;
                    case "gregory":
                        cal = new GregorianCalendar(zone, aLocale);
                        break;
                    }
                }
            }
            // 中间的代码省略.....
            return cal;
     }
    

    可以看出,getInstance()方法里面调用了createCalendar()方法来得到Calendar类的实例,最后返回给调用者。而createCalendar()方法中通过switch(){case}的判断来返回所对应的Calendar类的实例,这其实就是简单工厂模式的一种应用。

    看完简单工厂模式JDK中的应用之后,我们来设计一下自己的例子:

    小明家新开了一家小工厂,接了一单生意,帮助海尔(Haier)集团生产冰箱,并需要设计相应的方案。小明本身也是程序员出身,思考一会后就写出了下面的代码:

    /**
     * 冰箱
     */
    public interface IFridge {
    
        // 生产冰箱
        public void createFridge();
    
    }
    
    /**
     * 海尔
     */
    public class Haier implements IFridge {
        @Override
        public void createFridge() {
            System.out.println("生产海尔冰箱...");
        }
    }
    

    客户端调用代码:

    public static void main(String[] args) {
        IFridge iFridge = new Haier();
        iFridge.createFridge();
    }
    

    看上面的代码,父类IFridge类指向子类Haier类的引用,应用层需要依赖于Haier。如果业务扩展,后续增加格力(Gree)甚至更多,那么客户端这里的代码会越来越臃肿。所以,我们要想办法将这种依赖减弱,将创建IFridge对象的细节隐藏掉。我们用简单工厂模式优化一下:

    创建Gree格力类

    /**
     * 格力
     */
    public class Gree implements IFridge {
        @Override
        public void createFridge() {
            System.out.println("生产格力冰箱...");
        }
    }
    

    创建FridgeFactory工厂类

    /**
     * 冰箱工厂
     */
    public class FridgeFactory {
    
        // 创建对应的 IFridge 实例
        public static IFridge createFridge(String name){
            if ("haier".equals(name)){
                return new Haier();
            } else if ("gree".equals(name)){
                return new Gree();
            }
            return null;
        }
    }
    

    修改客户端调用的代码:

    public static void main(String[] args) {
        // 海尔
        IFridge haier = FridgeFactory.createFridge("haier");
        haier.createFridge();
    
        // 格力
        IFridge gree = FridgeFactory.createFridge("gree");
        gree.createFridge();
    }
    

    这样来看,虽然代码多了,但维护起来以及扩展起来就方便很多,来看一看类图:

    image20210618104712505.png

    当然,上面的FridgeFactory代码中依旧有些问题,如果我们需要增加生产美的(Midea)冰箱,那么我们就需要去修改createFridge()方法的代码,显然违背了开闭原则,我们来改造一下:

    修改FridgeFactory工厂类

    /**
     * 冰箱工厂
     */
    public class FridgeFactory {
    
        // 创建对应的 IFridge 实例
        public static IFridge createFridge(String className){
            try {
                if (null != className && !"".equals(className)){
                    // 反射
                    return (IFridge)Class.forName(className).newInstance();
                }
            }catch (Exception e){
                e.printStackTrace();
            }
            return null;
        }
    }
    

    修改客户端调用的代码

    public static void main(String[] args) {
        // com.xxx.Haier  换成  自己 项目中 Haier 所在的位置  海尔
        IFridge haier = FridgeFactory.createFridge("com.xxx.Haier");
        haier.createFridge();
    
        // com.xxx.Gree  换成  自己 项目中 Gree 所在的位置  格力
        IFridge gree = FridgeFactory.createFridge("com.xxx.Gree");
        gree.createFridge();
    }
    

    优化之后,我们再也不需要随着业务的提升而去修改FridgeFactory类中的代码了。但是依旧有一个问题,createFridge()方法中的参数是字符串,如果有人乱填怎么办,那不就报错了,所以再来优化一下:

    修改FridgeFactory工厂类

    /**
     * 冰箱工厂
     */
    public class FridgeFactory {
    
        // 创建对应的 IFridge 实例
        public static IFridge createFridge(Class<? extends IFridge> clazz){
            try {
                if (clazz != null){
                    return clazz.newInstance();
                }
            }catch (Exception e){
                e.printStackTrace();
            }
            return null;
        }
    }
    

    修改客户端调用的代码

    public static void main(String[] args) {
        // 海尔
        FridgeFactory.createFridge(Haier.class).createFridge();
    
        // 格力
        FridgeFactory.createFridge(Gree.class).createFridge();
    }
    

    再来看一下类图:

    image20210618110204756.png

    简单工厂模式虽然好用,但也有它的局限性:工厂类的职责过重,不利于扩展更为复杂产品结构。

    2.2 工厂方法模式

    定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类,工厂方法让类的实例化推迟到子类中进行。

    在工厂方法模式中用户只需要关心所需产品对应的工厂,无须关心创建细节,而且加入新的产品符合开闭原则

    随着小明家新工厂的生意火爆,各类的订单都纷涌而至,各个牌子的厂家都想让小明家的工厂生产冰箱,小明无奈只能开了分工厂,并根据客户的品牌名给工厂取了对应的名字,其中海尔工厂生产海尔的冰箱,格力工厂生产格力的冰箱,美的工厂生产美的的冰箱。用代码演化就是下面这般:

    IFridgeFactory类接口

    public interface IFridgeFactory {
        public IFridge createIFridge();
    }
    

    海尔

    // 海尔  工厂
    public class HaierFactory implements IFridgeFactory {
        @Override
        public IFridge createIFridge() {
            return new Haier();
        }
    }
    

    格力

    // 格力  工厂
    public class GreeFactory implements IFridgeFactory {
        @Override
        public IFridge createIFridge() {
            return new Gree();
        }
    }
    

    美的

    /**
     * 美的
     */
    public class Midea implements IFridge {
        @Override
        public void createFridge() {
            System.out.println("生产美的冰箱...");
        }
    }
    
    // 美的
    public class MideaFactory implements IFridgeFactory {
        @Override
        public IFridge createIFridge() {
            return new Midea();
        }
    }
    

    客户端调用:

    public static void main(String[] args) {
        // 格力
        new GreeFactory().createIFridge().createFridge();
    
        // 海尔
        new HaierFactory().createIFridge().createFridge();
    
        // 美的
        new MideaFactory().createIFridge().createFridge();
    }
    

    这里其实就是细化了工厂,将业务拆分,利用了设计模式原则中的单一职责原则,让每个品牌对应工厂只干一件事,不去掺和其他品牌的事情。来看一看类图:

    image20210618113203319.png

    工厂方法模式适用于一下场景:

    • 创建对象需要大量重复的代码
    • 客户端(应用层)不依赖于产品类实例如何被创建、实现等细节
    • 一个类通过其子类来指定创建哪个对象

    工厂方法模式也有缺点:

    • 类的个数容易过多,增加复杂度
    • 增加了系统的抽象性和理解难度

    2.3 抽象工厂模式

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

    这个定义读起来相当的拗口,很抽象,不好理解。还是和上面的例子结合来说明:

    在生产完一批冰箱并上市售卖之后,美的、格力、海尔等公司非常满意,慢慢的将自己家的空调、热水器也交给小明家的工厂去生产了。小明为此在对应的品牌工厂有开辟了对应的生产设备的空间(这里为了大家看的方便,我将所有的代码都放上去):

    冰箱、空调、热水器接口

    // 冰箱
    public interface IFridge {
        // 生产冰箱
        public void createFridge();
    }
    
    // 空调
    public interface IAirConditioner {
        // 生产空调
        public void createAirConditioner();
    }
    
    // 热水器
    public interface IWaterHeater {
        // 生产热水器
        public void createWaterHeater();
    }
    

    海尔

    /**
     * 海尔 冰箱
     */
    public class HaierFridge implements IFridge{
        @Override
        public void createFridge() {
            System.out.println("生产海尔冰箱...");
        }
    }
    
    // 海尔 空调
    public class HaierAirConditioner implements IAirConditioner {
        @Override
        public void createAirConditioner() {
            System.out.println("生产海尔空调...");
        }
    }
    
    // 海尔热水器
    public class HaierWaterHeater implements IWaterHeater {
    
        @Override
        public void createWaterHeater() {
            System.out.println("生产海尔热水器...");
        }
    }
    

    格力

    /**
     * 格力 冰箱
     */
    public class GreeFridge implements IFridge {
        @Override
        public void createFridge() {
            System.out.println("生产格力冰箱...");
        }
    }
    
    // 格力 空调
    public class GreeAirConditioner implements IAirConditioner {
        @Override
        public void createAirConditioner() {
            System.out.println("生产格力空调...");
        }
    }
    
    // 格力热水器
    public class GreeWaterHeater implements IWaterHeater {
        @Override
        public void createWaterHeater() {
            System.out.println("生产格力热水器...");
        }
    }
    

    美的

    /**
     * 美的 冰箱
     */
    public class MideaFridge implements IFridge{
        @Override
        public void createFridge() {
            System.out.println("生产美的冰箱...");
        }
    }
    
    // 美的 空调
    public class MideaAirConditioner implements IAirConditioner {
        @Override
        public void createAirConditioner() {
            System.out.println("生产美的空调...");
        }
    }
    
    // 美的热水器
    public class MideaWaterHeater implements IWaterHeater {
        @Override
        public void createWaterHeater() {
            System.out.println("生产美的热水器...");
        }
    }
    

    工厂接口

    public interface IFactory {
    	// 冰箱
        public IFridge createIFridge();
    	// 空调
        public IAirConditioner createIConditioner();
    	// 热水器
        public IWaterHeater createIWaterHeater();
    }
    

    海尔工厂

    // 海尔  工厂
    public class HaierFactory implements IFactory {
        // 冰箱
        @Override
        public IFridge createIFridge() {
            return new HaierFridge();
        }
    
        // 空调
        @Override
        public IAirConditioner createIConditioner() {
            return new HaierAirConditioner();
        }
    
        // 热水器
        @Override
        public IWaterHeater createIWaterHeater() {
            return new HaierWaterHeater();
        }
    }
    

    格力工厂

    // 格力
    public class GreeFactory implements IFactory {
        // 冰箱
        @Override
        public IFridge createIFridge() {
            return new GreeFridge();
        }
    	// 空调
        @Override
        public IAirConditioner createIConditioner() {
            return new GreeAirConditioner();
        }
    	// 热水器
        @Override
        public IWaterHeater createIWaterHeater() {
            return new GreeWaterHeater();
        }
    }
    

    美的工厂

    // 美的
    public class MideaFactory implements IFactory {
        // 冰箱
        @Override
        public IFridge createIFridge() {
            return new MideaFridge();
        }
    	// 空调
        @Override
        public IAirConditioner createIConditioner() {
            return new MideaAirConditioner();
        }
        // 热水器
        @Override
        public IWaterHeater createIWaterHeater() {
            return new MideaWaterHeater();
        }
    }
    

    客户端调用

    public static void main(String[] args) {
    	// 海尔工厂
        HaierFactory haierFactory = new HaierFactory();
        haierFactory.createIFridge().createFridge();
        haierFactory.createIConditioner().createAirConditioner();
        haierFactory.createIWaterHeater().createWaterHeater();
    
    	// 格力工厂
        GreeFactory greeFactory = new GreeFactory();
        greeFactory.createIFridge().createFridge();
        greeFactory.createIConditioner().createAirConditioner();
        greeFactory.createIWaterHeater().createWaterHeater();
    
        // 美的工厂
        MideaFactory mideaFactory = new MideaFactory();
        mideaFactory.createIFridge().createFridge();
        mideaFactory.createIConditioner().createAirConditioner();
        mideaFactory.createIWaterHeater().createWaterHeater();
    }
    

    类图

    image20210618145044765.png

    从上面一大堆的代码,尤其是类图,我们可以很明显的感觉到,抽象工厂可以完美清晰的描述海尔、格力、美的三个品牌的冰箱、空调、热水器的庞大体系。但也正因为如此,抽象工厂给我们的视觉冲击有些大,能很明显的感觉到系统的复杂性抽象性以及系统的极难扩展性;并且这里还隐藏着一个违背开闭原则的问题:

    在工厂接口IFactory类中,如果在日后的产品升级当中,需要增加生产洗衣机的业务,那这里修改之后,所有实现IFactory接口的类都需要变动,很大程度增加了系统的不稳定性。

    也正因为如此,在实际的业务开发中,我们不应该有着强烈的强迫症和洁癖,认为一个系统的结构设计必须要完美的符合各种原则。要结合实际的业务去思考,如果系统结构的等级更新不频繁的话,不遵守某些原则也是有必要性的,毕竟所有的技术都是为业务而服务的

  • 相关阅读:
    Spark Streaming 中管理 Kafka Offsets 的几种方式
    Kafka 在华泰证券的探索与实践
    Kafka 客户端是如何找到 leader 分区的
    关于Java中如何判断字符串是否为json格式
    关于JAVA递归遍历树级菜单结构
    关于JDK8 三种时间获取方法以及日期加减
    关于在Java里面,时间的一些业务
    关于Java的IO通信
    关于git同时提交到2个仓库gitee github
    关于JVM——工具(二)
  • 原文地址:https://www.cnblogs.com/caimm/p/14899740.html
Copyright © 2011-2022 走看看