设计模式学习02—工厂模式

1、动机与定义

     我们在程序中使用一个对象时,须要new一下,假设须要设置其它值就再初始化一下。比方我要使用一个button,手动new一个矩形button。然后初始化一些值,如显示文字,背景色等。
        // 矩形button
        IButton btn = new RecButton();
        // 初始化其它值
        btn.setText("提交");
        btn.setBackgroundColor("#00aaff");
        // 其它初始化省略

        // 圆形button
        IButton btn2 = new RoundButton();
        btn.setText("关于");
        btn.setBackgroundColor("#00aaff");
        // 其它初始化省略
     这样写有几个缺点:
     1、写一次没有问题,假设须要100个,就要写100次,太麻烦了。
     2、非常多设置是反复的,比方样例中的背景色。一个系统可能风格统一,仅仅有几种背景色,不须要每次都手动设置。
     3、耦合性太强,client必须知道详细的button创建过程,必须会创建button才行,兴许button的方法改变,client也可要跟着改动,如以后button必须设置大小了,全部client代码都要变动。
     4、反复对象没有控制。比方btn2的关于。可能每一个页面都有。可是每一个页面的这个button都是一模一样的,不是必需每次都创建一遍。

     5、没有封装变化,假如写了100个new RoundButton,兴许这个button发生改变了,我们要改100处代码。
     等等,假设你再细致想想,各种各样的情况下都有各种各样的缺点(当然这么写也有长处的,至少简单嘛,怎样设计没有最好。仅仅有合适的)。那么我们有没有其它方式来规避这些问题呢?事实上我们须要一个对象时,除了自己new之外,还有就是从其它地方获取,我们全然能够把这些button的创建过程放到一起,client使用的时候直接获取即可了。比方以下代码:
public class RoundButtonFactory implements Creater {

    public static IButton createButton(String text) {
        // 圆形button
        IButton btn = new RoundButton();
        btn.setText(text);
        btn.setBackgroundColor("#00aaff");
        return btn;
    }
}
     工厂模式定义:定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类。
     客户端使用时,仅仅须要调用createButton即可,屏蔽了底层的详细实现。兴许实现类变化了,仅仅须要改动这种方法即可了;也使client和详细的button实现类解耦开来。事实上这就是最主要的工厂模式。
     简单说就是将要使用的对象抽象出一个接口(产品),另一个接口的创建工厂,每一个详细实现类(产品的实现类)的创建由工厂的实现类创建。


2、结构与类图

     工厂模式通用类图例如以下:

     上面举的样例的类图例如以下:

     工厂方法包括四个角色:
     1、抽象产品(Product):负责定义产品公有属性。
     2、详细产品(ConcreteProduct):详细的产品实现类;
     3、抽象工厂(Creater):抽象的创建类。也就是抽象工厂;
     4、详细工厂(ConcreteCreater):详细创建者。也就是详细工厂,负责详细产品实现类的创建。

3、适用场景及效果(优缺点)

     没有工厂的时候,假如我们要做饭,须要用到火,创建火的同一时候发现须要用到木柴,还要创建一个锯来锯木柴......代码例如以下:
        // 创建锯
        Saw saw = new Saw ();
        // 使用锯,锯木柴
        FireWood fw = saw.cut();
        // 使用木柴创建火
        Fire fire = new WoodFire(fw);
     能够看到。这种话,做饭的逻辑就依赖了锯、木柴、火等东西,假设使用工厂呢
        Fire fire = WoodFireFactroy.create();
     1、具有良好的封装性,逻辑代码清晰,不用new了,不用初始化了,仅仅须要简单的get或者create即可了。
     2、耦合性低,工厂模式是典型的解耦框架。屏蔽了详细产品类(都是用产品接口嘛),调用者无需关心底层怎样实现,产品类变化时仅仅要接口不变就不影响调用者,使client和详细产品解耦,更能屏蔽创建详细产品时须要的其它关联类(如做饭就不须要依赖锯了),符合迪米特法则。仅仅和须要的类交流。也符合依赖导致原则,仅仅依赖抽象。更符合里氏替换原则。使用产品子类取代父类。全然没问题。
     3、扩展很方便,新加一类产品时(如上面样例新增一种button),仅仅须要新增一个工厂实现类就可以。无需改动原有代码,达到了“拥抱变化”,符合开闭原则。
     当产品创建简单,比較固定的时候。或者调用者个性化情况太多时,工厂就体现不出他的优势了,比方经常使用的List,我们就不是必需弄个ListFactory.createArrayList()。徒增代码复杂性。

还有种类比較固定。不会太多,不是必需抽象出接口,那也不必非用工厂模式。最好在以下的情况下才考虑使用工厂模式:

     1、调用者不须要直到详细的产品创建过程时;
     2、调用者使用的对象存在变动的可能,甚至全然不知道使用哪个详细对象时。

     3、须要做出灵活、可扩展的功能时。再考虑工厂模式。事实上不一定全部功能都要做到可扩展。慎重过度设计。

     4、须要解耦时,降低调用者和详细实现类的依赖时。

4、演示样例和扩展

     1、退化成简单工厂模式,当要创建的产品种类较少时,而且能够预见时,能够把工厂实现类合并到一起,对外提供一个静态工厂方法。比方上面的button样例中:
public static IButton crateButton(String type, String text) {
        IButton btn = null;
        if ("round".equals(type)) {
            btn = new RoundButton(); // 圆形button
        } else if ("rec".equals(type)) {
            btn = new RecButton(); // 矩形button
        } else {
            return null ;
        }

        btn.setText(text);
        btn.setBackgroundColor("#00aaff");
        return btn;
    }
       这样的简单工厂模式用起来很easy。缺点是扩展困难,不符合开闭原则,要注意设计没有最好,仅仅有适不适合,在可预见的变化下,简单工厂模式很好用。
     2、约束产品类实例数量,通常和其它模式组合能达到非常多效果,比方使用工厂创建好对象后缓存起来,达到单例或多例的目的。比方以下单例工厂:
//单例工厂
public class SingletonFactory {

    private static Map<Class<?

>, Object> objCache = new HashMap<Class<?>, Object>(); public synchronized static Object getInstance(Class<?> clazz) throws Exception { Object singleton = objCache.get(clazz); if (singleton == null) { singleton = createInstance(clazz); objCache.put(clazz, singleton); } return singleton; } private static Object createInstance(Class<?> clazz) throws Exception { Constructor ct = clazz.getDeclaredConstructor(); ct.setAccessible( true); return ct.newInstance(); } }

     3、多工厂协调,工厂模式中。一个工厂创建一个产品也行。创建多个产品也行。当产品种类过多时,假设工厂类也较多,此时最好弄一个协调类来协调,方便调用者使用。而不是让调用者逐个去找工厂类。

     单例能够。多例也就没问题了,比方数据库连接池。设置最大100个,使用工厂模式就非常有效,此时须要考虑每一个实例的状态。使用中的话不能被获取等等。
     4、延迟实例化,有的时候产品创建和销毁比較耗费资源,能够考虑创建好之后缓存起来,用完之后不销毁,或者使用完成后将对象改成初始状态,而不是又一次创建,方便兴许使用,还是连接池的样例。假设用完了,是不销毁的,还会又一次使用。
     5、结合反射或配置文件,取代程序new。尽管样例中我们使用的是new创建对象,可是在现实编程中,大部分工厂都是配合反射来使用的,能够考虑将要创建的产品属性,设置工厂属性放到配置文件里,程序启动就将对象创建好。这样当添加一个简单产品时。能够做到改动配置文件即可,就算添加复杂产品,仅仅须要新写一个工厂类,配置配置即可。而不用大量改动源代码。
     结束语,工厂方法事实上在项目中使用很很频繁,这个模式差点儿人尽皆知,但却不是每一个人都能用好,工厂模式通常和其它模式混合使用,变化出无穷的优秀设计。

原文地址:https://www.cnblogs.com/wzzkaifa/p/6840020.html