美文网首页玩转设计模式
第二章——工厂方法模式

第二章——工厂方法模式

作者: 博尔特uncle | 来源:发表于2017-10-24 15:58 被阅读0次

    又是新的一天,今天是19大闭幕式,呵呵我的股票长了点,唉总算是收回了点成本,我是实践中深刻体会到了这句话的内涵,股市有风险,入市需谨慎;还是少玩的好;今天我们来玩玩工厂方法模式.
    先看下定义:
    工厂方法模式定义:Define an interface for creating an object,but let subclasses decide which class to
    instantiate.Factory Method lets a class defer instantiation to subclasses.(定义一个用于创建对象的
    接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。),

    工厂方法模式是一个应用最广泛的设计模式,也是一个高度解耦的设计模式:咱们今天从代码中展开,相信任何一个有经验的程序员一看就了然。
    今天同事准备买电脑让我帮他推荐个电脑,咱们就以电脑为例吧:

      public interface Computer {
    /**
     * 电脑型号
     * 
     * @return
     */
    public String getModelNumber();
    
    /**
     * 电脑出厂价
     * 
     * @return
     */
    public String getComputerPrice();
    
    /**
     * 电脑的基本配置
     * 
     * @return
     */
    public String getComputerDesc();
         }
    
    
     public class InspirationComputer implements Computer {
    @Override
    public String getModelNumber() {
        return "灵越系列Ins15E-3725";
    }
    
    @Override
    public String getComputerPrice() {
        
        return "3333元";
    }
    
    @Override
    public String getComputerDesc() {
    
        return "适用场景 家庭影音 女性定位 轻薄便携 学生 商务办公 高清游戏 家庭娱乐";
    }
       }
         public class VostroComputer implements Computer {
    
    @Override
    public String getModelNumber() {
        // TODO Auto-generated method stub
        return "Vostro 5459-1528";
    }
    
    @Override
    public String getComputerPrice() {
        // TODO Auto-generated method stub
        return "¥ 4499.00";
    }
    
    @Override
    public String getComputerDesc() {
        // TODO Auto-generated method stub
        return "适用场景     商务办公 家庭娱乐";
    }}
    

    戴尔工厂里生产电脑;那么电脑有型号有价格,和基本配置定位;这是产品的共有特性;我们用一个接口来统一定义这些方法;注意面向接口编程一个好处就是便于扩展;有了电脑就应该有工厂去生产:

                /**
                  *  抽象工厂上层
                 *
                 */      
         public abstract class AbsrtactComputerFactory {
    // 限制传入类型只能是Computer的子类
       public abstract <T extends Computer> T createComputer(Class<T> c);
           }
    
        /**
        * 具体的工厂方法实现,目的就是生产电脑
       * 
        * @author Administrator
      *
      */
    
      public class ComputerFactory extends AbsrtactComputerFactory {
    @Override
    public <T extends Computer> T createComputer(Class<T> c) {
        Computer computer = null;
        try {
            computer = (T) Class.forName(c.getName()).newInstance();
        } catch (Exception e) {
    
            e.printStackTrace();
        }
    
        return (T) computer;
    }}
    

    好了现在我们有了工厂了,可以开工了:

        public static void main(String[] args) {
        // TODO Auto-generated method stub
        AbsrtactComputerFactory compFactory = new ComputerFactory();
        Computer inspirationComputer = compFactory.createComputer(InspirationComputer.class);
        System.out.println(inspirationComputer.getComputerDesc() + "\n" + inspirationComputer.getModelNumber() + "\n"
                + inspirationComputer.getComputerPrice());
        Computer vostroComputer = compFactory.createComputer(VostroComputer.class);
        System.out.println(vostroComputer.getComputerDesc() + "\n" + vostroComputer.getModelNumber() + "\n"
                + vostroComputer.getComputerPrice());
    }
    
    图片.png

    由图可见,我们构造了一个工厂生产了两台不同型号的电脑,简单吧,这就是工厂方法模式的通用用法。

    列举下他的优点吧:
    1.良好的封装性,代码结构清晰;
    一个对象创建是有条件约束的,如一个调用者需要一个具体的产品对象,只要知道这个产品的类名(或约束字符串)就可以了,不用知道创建对象的艰辛过程,降低模块间的耦合;对于调用者来说我们只需要知道生产什么型号的电脑,知道类名就可以,不需要关注这个类怎么创建的.
    2.扩展性非常优秀;
    就代码而言,如果我们需要生产一个X型号的电脑,具有特殊特性不具备公共特性怎么办?我们只需要增加一个X型即可,并且再此进行特性扩展,不需要修改工厂生产的上层封装;
    如果盘子大了怎么办?遇到业务代码很多,我们是不是可以考虑开辟不同的工厂专司其职呢,比如我们想戴尔灵越系列专职由一个工厂生产,Vostro系列专由一个工厂生产,结构更清晰,我们完全可以创建不同的工厂去做,这也是面向接口编程的好处,但是也有弊端,因为维护成本就高了,产品和工厂是一一对应的关系,有几个产品就要创建几个工厂.

    3.屏蔽产品类
    产品类的实现如何变化,调用者都不需要关心,它只需要关心产品的接口,只要接口保持不变,系统中的上层模块就不会发生变化,也就是从上到下的过程,

    以上是工厂方法模式的标准写法,下面我们看看工厂方法模式的几个变种使用:

    1.简单工厂模式:
    每次都要创建一个工厂太费劲了,我们把工厂的抽象去掉,创建的方法改成静态:

       public class ComputerFactory  {
       public static <T extends Computer> T createComputer(Class<T> c) {
        Computer computer = null;
        try {
            computer = (T) Class.forName(c.getName()).newInstance();
        } catch (Exception e) {
    
            e.printStackTrace();
        }
    
        return (T) computer;
    }
     }
            
    
    
    
         public static void main(String[] args) {
        // TODO Auto-generated method stub
        Computer comp=ComputerFactory.createComputer(InspirationComputer.class);
          //        AbsrtactComputerFactory compFactory = new ComputerFactory();
     //     Computer inspirationComputer = compFactory.createComputer(InspirationComputer.class);
    //      System.out.println(inspirationComputer.getComputerDesc() + "\n" + inspirationComputer.getModelNumber() +           "\n"
                    //              + inspirationComputer.getComputerPrice());
                //      Computer vostroComputer = compFactory.createComputer(VostroComputer.class);
             //     System.out.println(vostroComputer.getComputerDesc() + "\n" + vostroComputer.getModelNumber() + "\n"
           //               + vostroComputer.getComputerPrice());
        System.out.println(comp.getComputerDesc() + "\n" + comp.getModelNumber() + "\n"
                + comp.getComputerPrice());
    } 
    

    现在是不是清爽了许多,这种方式应用还是很广泛的,叫做简单工厂模式,但是工厂类的扩展就不那么方便了

    2.多工厂模式:我们还是看代码

       public abstract class AbsrtactComputerFactoryzz {
    public abstract Computer createComputer();}
      
    
    
        public class InspirationFactory extends AbsrtactComputerFactoryzz {
    
    @Override
    public Computer createComputer() {
        return new InspirationComputer();
    }}
    

    我们创建了抽象工厂类,工厂里的方法还是生产电脑,这一点不容置疑,就像多年前一个前辈说的,人在黑板上花圆,现在有人,黑板,圆三个对象,画圆的方法应该谁提供呢?很明显是圆,圆包含半径,圆心,自然是它;
    由于是多工厂模式,自然各个工厂各司其职,在子类中写死这个工厂干什么的就行了;

    3.替代单例模式

    故名思议,这是一种可以用工厂方式做到单例设计模式的一种工厂变体,其实这是不明智的,开发有开发的规矩,不能使用一些非正常手段去实现通常写法,但是也要了解一下,我们看下代码:

       public class CommonFactoryTest {
    private static CommonFactoryTest singleton;
    static {
        try {
            Class cl = Class.forName(CommonFactoryTest.class.getName());
            // 获得无参构造
            Constructor constructor = cl.getDeclaredConstructor();
            // 设置无参构造是可访问的
            constructor.setAccessible(true);
            // 产生一个实例对象
            singleton = (CommonFactoryTest) constructor.newInstance();
        } catch (Exception e) {
            // 异常处理
        }
    }
    
    public static CommonFactoryTest getSingleton() {
        return singleton;
    }}
    

    我们用暴力反射技术,和classload的机制保证内存中只有一个实例存在,但是很明显效率是相当低的,不应该这么干。

    4.延迟初始化,

    一个对象被消费完毕后,并不立刻释放,工厂类保持其初始状态,等待再次被使用;
    比如说我们不希望一个对象频繁销毁,想复用时候,比如做内存缓存的时候,但是应当注意内存泄漏的可能;比如Android开发中首页tab的fragment我们就可以用这种方式保存其产品的状态不被释放以利复用;下面看一下代码:

             public static final int PLAYSHARE_SFRAGMENT = 0;
    public static final int DISCUSS_FRAGMENT = 1;
    public static final int MARKET_FRAGMENT = 2;
    public static final int CONSULTING = 3;
    public static final int MY_FRAGMENT = 4;
    private static SparseArray<BaseFragment> mFragments = new SparseArray<BaseFragment>();
    public static BaseFragment createFragment(int position) {
        BaseFragment fragment = mFragments.get(position);
        if (fragment == null) {
            switch (position) {
                case PLAYSHARE_SFRAGMENT:
                    fragment = new PlaySharesFragment();
                    break;
                case DISCUSS_FRAGMENT:
                    fragment = new DiscussFragment();
                    break;
                case MARKET_FRAGMENT:
                    fragment = new QuotationFragment();
                    break;
                case CONSULTING:
                    fragment = new ConsultingFragment();
                    break;
                case MY_FRAGMENT:
                    fragment = new MyFragment();
                    break;
                default:
                    break;
            }
            mFragments.put(position, fragment);
        }
        return fragment;
    }
    

    很明显,代码对我们需要的产品进行了缓存再利用;无论你用Map容器也好还是其他List集合也好,原理都一样都是希望对已有产品的再利用,这个方式应用也很广泛,但是应当注意缓存的释放;

    工厂方法模式总结:
    是典型的解耦框架,高层模块只需要知道产品的抽象类,其他的实
    现类都不用关心,符合迪米特法则,我不需要的就不要去交流;也符合依赖倒置原则,只依
    赖产品类的抽象;当然也符合里氏替换原则,使用产品子类替换产品父类

    使用场景:
    万物皆对象,工厂方法模式是用来生产对象,他可以替代new Object ,任何使用 new 对象的地方我们都可以用工厂模式替代,但是我们要考虑是否值得这么做,因为这样对于开发者不也是增加了代码复杂度吗?
    Ok今天就先到这里了,

    相关文章

      网友评论

        本文标题:第二章——工厂方法模式

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