java/android 设计模式学习笔记(3)---工厂方法模式

  这篇来介绍一下工厂方法模式(Factory Method Pattern),在实际开发过程中我们都习惯于直接使用 new 关键字用来创建一个对象,可是有时候对象的创造需要一系列的步骤:你可能需要计算或取得对象的初始设置;选择生成哪个子对象实例;或在生成你需要的对象之前必须先生成一些辅助功能的对象,这个时候就需要了解该对象创建的细节,也就是说使用的地方与该对象的实现耦合在了一起,不利于扩展,为了解决这个问题就需要用到我们的工厂方法模式,它适合那些创建复杂的对象的场景,工厂方法模式也是一个使用频率很高的设计模式。

  PS:对技术感兴趣的同鞋加群544645972一起交流。

设计模式总目录

  java/android 设计模式学习笔记目录

特点

  工厂方法模式(Factory Method Pattern)定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个,工厂方法让类把实例化推迟到子类,这样的设计将对象的创建封装其来,以便于得到更松耦合,更有弹性的设计。

  工厂方法模式是创建型设计模式之一,是结构较为简单的一种模式,在我们平时的开发过程中应用也是非常的广泛,比如 ArrayList,HashSet,与 Iterator 之间就能算是一种工厂方法。

  简单工厂模式(Simple Factory)是工厂方法模式的一种,工厂方法模式的特点总结一下:

  • 简单工厂模式从某种意义上来说不算是真正的设计模式,但仍不失为一个简单的方法,可以将客户程序从具体类中解耦;
  • 工厂方法模式使用继承,把对象的创建委托给子类,子类实现工厂方法来创建对象,也就是说允许将实例化延迟到子类进行;;
  • 工厂方法模式是一个非常典型的“针对抽象编程,而不是具体类编程”例子。

UML类图

  

  上图为工厂方法模式的uml类图,几个角色的分工也很明确,主要分为四大模块:

  • 一是抽象工厂接口,其为工厂方法模式的核心,它定义了一个工厂类所具备的基本行为;
  • 二是具体工厂,其实现了具体的业务逻辑;
  • 三是抽象产品接口,它定义了所有产品的公共行为;
  • 四是具体产品,为实现抽象产品的某个具体产品的对象。

简单工厂模式和工厂方法模式的区别就在于简单工厂模式将抽象工厂接口这个角色给精简掉了,是工厂方法模式的一个弱化版本。

  从这种设计的角度来思考,工厂方法模式是完全符合设计原则的,它将对象的创建封装起来,以便于得到更松耦合,更有弹性的设计,而且工厂方法模式依赖于抽象的接口,将实例化的任务交给子类去完成,有非常好的可扩充性。

示例与源码

  我们以一个简单的玩具工厂为例,工厂中生产小孩的玩具,女生的玩具和男生的玩具,先写一个 IToy 的抽象产品接口用来定义玩具的基本行为模式,然后实现该接口生成几个玩具的具体产品类 ChildrenToy,MenToy 和 WomenToy 类:

IToy.class

public interface IToy {
    /**
     * 名字
     */
    String getName();

    /**
     * 价格
     */
    float price();

    /**
     * 玩
     */
    void play();
}

ChildrenToy.class

public class ChildrenToy implements IToy{
    @Override
    public String getName() {
        return "toy car";
    }

    @Override
    public float price() {
        return 10.5f;
    }

    @Override
    public void play() {
        Log.e("play", "a child is playing a toy car");
    }
}

MenToy.class

public class MenToy implements IToy{
    @Override
    public String getName() {
        return "PS4";
    }

    @Override
    public float price() {
        return 2300;
    }

    @Override
    public void play() {
        Log.e("play", "a man is playing GTA5 on ps4");
    }
}

WomenToy.class

public class WomenToy implements IToy{
    @Override
    public String getName() {
        return "plush toy";
    }

    @Override
    public float price() {
        return 200;
    }

    @Override
    public void play() {
        Log.e("play", "a woman is playing with a plush toy");
    }
}

完成产品的两个角色之后,接下来要定义工厂类的两个角色,根据工厂方法模式和简单工厂模式的不同,可以有两种不同的写法:

工厂方法

  工厂方法模式需要先写出一个工厂类的抽象接口来定义行为,这个时候根据实际情况我们可以分为两种实现方式,第一种写法会有多个 ConcreteFactory 的角色;第二种写法只会有一个 ConcreteFactory 的角色,根据传入的参数不同而返回不同的产品对象:

multi ConcreateFactory

IToyCreator.class

public interface IToyCreator {
    /**
     * 生产玩具
     */
    IToy createToy();
}

ChildrenToyCreator.class

public class ChildrenToyCreator implements IToyCreator {
    private static final String TAG = "ChildrenToyCreator";

    @Override
    public IToy createToy() {
        IToy toy = new ChildrenToy();
        Log.e(TAG, "buy a/an " + toy.getName()+" for " + toy.price() + " yuan, and then ---");
        toy.play();
        return toy;
    }
}

MenToyCreator.class

public class MenToyCreator implements IToyCreator  {
    private static final String TAG = "MenToyCreator";

    @Override
    public IToy createToy() {
        IToy toy = new MenToy();
        Log.e(TAG, "buy a/an " + toy.getName()+" for " + toy.price() + " yuan, and then ---");
        toy.play();
        return toy;
    }
}

WomenToyCreator.class

public class WomenToyCreator implements IToyCreator  {
    private static final String TAG = "WomenToyCreator";

    @Override
    public IToy createToy() {
        IToy toy = new WomenToy();
        Log.e(TAG, "buy a/an " + toy.getName()+" for " + toy.price() + " yuan, and then ---");
        toy.play();
        return toy;
    }
}

最后直接可以根据需要创建不同的 IToy 对象了,测试代码如下:

IToyCreator toyCreator;
switch (v.getId()) {
    case R.id.btn_child:
        toyCreator = new ChildrenToyCreator();
        toyCreator.createToy();
        break;
    case R.id.btn_men:
        toyCreator = new MenToyCreator();
        toyCreator.createToy();
        break;
    case R.id.btn_women:
        toyCreator = new WomenToyCreator();
        toyCreator.createToy();
        break;
}

single ConcreteFactory

IToyCreator.class

public interface IToyCreator {
    /**
     * 生产玩具
     */
    <T extends IToy> IToy createToy(Class<T> clazz);
}

ConcreteToyCreator.class

public class ConcreteToyCreator implements IToyCreator{
    private static final String TAG = "ConcreteToyCreator";

    @Override
    public <T extends IToy> IToy createToy(Class<T> clazz) {
        if (clazz == null){
            throw new IllegalArgumentException("argument must not be null");
        }
        try {
            IToy toy = clazz.newInstance();
            Log.e(TAG, "buy a/an " + toy.getName()+" for " + toy.price() + " yuan, and then ---");
            toy.play();
            return toy;
        } catch (Exception e) {
            throw new UnknownError(e.getMessage());
        }
    }
}

这种写法直接传入一个 Class 对象,接着利用反射的方式进行对象的创建,可以说从某种意义上精简了很多的工厂实现类,不用一个具体产品类就对应需要一个具体工厂类了,下面为测试代码:

IToyCreator toyCreator;
switch (v.getId()) {
    case R.id.btn_child:
        toyCreator.createToy(ChildrenToy.class);
        break;
    case R.id.btn_men:
        toyCreator.createToy(MenToy.class);
        break;
    case R.id.btn_women:
        toyCreator.createToy(WomenToy.class);
        break;
}

总结对比

  以上的两种方式当然最后都能够成功打印出正确的结果:

  

单个工厂实现类的方法对比前面的多个工厂实现类的方法来说更加的简洁和动态,而且对于以后新增的产品类来说也能够不用修改原来的代码,符合开闭原则,但是这种写法在某些情况下是不适用的,比如不同的 IToy 对象设置了不同的构造函数,参数都不一样,用反射来实现就不适用了,这个时候就只能为每一个具体产品类都定义一个对应的具体工厂类了

简单工厂

  同样是上面的代码,具体工厂实现类只有一个的时候,我们还是为工厂提供了一个抽象类,那么,如果将 IToyCreator 这个角色精简掉,只留下 ConcreteToyCreator 的这个角色,将其中的产品生成方法设置为静态应该也是没问题的:

public class ToyCreator{
    private static final String TAG = "ToyCreator";

    public static <T extends IToy> IToy createToy(Class<T> clazz) {
        if (clazz == null){
            throw new IllegalArgumentException("argument must not be null");
        }
        try {
            IToy toy = clazz.newInstance();
            Log.e(TAG, "buy a/an " + toy.getName()+" for " + toy.price() + " yuan, and then ---");
            toy.play();
            return toy;
        } catch (Exception e) {
            throw new UnknownError(e.getMessage());
        }
    }
}

像这样的方式就称为简单工厂模式,上面也说过,是工厂方法模式的一个弱化版本,缺点就是失去了被子类继承的特性,所有的压力都集中在工厂类中,不利于维护。

总结

  总的来说,工厂方法模式是一个很好的设计模式,它遵循了一个“尽可能让事情保持抽象”的原则,松耦合的设计原则也能够很好的符合开闭原则,将类的实例化推迟到子类,同时也摈弃了简单工厂模式的缺点。

  但是同时工厂方法模式也有一些缺点,每次我们为工厂方法添加新的产品时就要编写一个新的产品类,同时还要引入抽象层,当产品种类非常多时,会出现大量的与之对应的工厂对象,这必然会导致类结构的复杂化,所以对于简单的情况下,使用工厂方法模式就需要考虑是不是有些“重”了。

源码下载

  https://github.com/zhaozepeng/Design-Patterns/tree/master/FactoryMethodPattern

引用

http://blog.csdn.net/jason0539/article/details/23020989

https://en.wikipedia.org/wiki/Factory_method_pattern

http://news.tuxi.com.cn/to/kf/satmaj/namtst.html

时间: 2024-08-02 02:49:11

java/android 设计模式学习笔记(3)---工厂方法模式的相关文章

java/android 设计模式学习笔记(4)---抽象工厂模式

再来介绍一下抽象工厂模式(Abstact Factory Pattern),也是创建型模式之一,上篇博客主要介绍了工厂方法模式.抽象工厂模式和工厂方法模式稍有区别.工厂方法模式中工厂类生产出来的产品都是具体的,也就是说每个工厂都会生产某一种具体的产品,但是如果工厂类中所生产出来的产品是多种多样的,工厂方法模式也就不再适用了,就要使用抽象工厂模式了. 抽象工厂模式的起源或者最早的应用,是对不同操作系统的图形化解决方案,比如在不同操作系统中的按钮和文字框的不同处理,展示效果也不一样,对于每一个操作系

java/android 设计模式学习笔记(10)---建造者模式

这篇博客我们来介绍一下建造者模式(Builder Pattern),建造者模式又被称为生成器模式,是创造性模式之一,与工厂方法模式和抽象工厂模式不同,后两者的目的是为了实现多态性,而 Builder 模式的目的则是为了将对象的构建与展示分离.Builder 模式是一步一步创建一个复杂对象的创建型模式,它允许用户在不知道内部构建细节的情况下,可以更精细地控制对象的构造流程.一个复杂的对象有大量的组成部分,比如汽车它有车轮.方向盘.发动机.以及各种各样的小零件,要将这些部件装配成一辆汽车,这个装配过

java/android 设计模式学习笔记(一)---单例模式

前段时间公司一些同事在讨论单例模式(我是最渣的一个,都插不上嘴 T__T ),这个模式使用的频率很高,也可能是很多人最熟悉的设计模式,当然单例模式也算是最简单的设计模式之一吧,简单归简单,但是在实际使用的时候也会有一些坑. PS:对技术感兴趣的同鞋加群544645972一起交流 设计模式总目录 java/android 设计模式学习笔记目录 特点 确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 单例模式的使用很广泛,比如:线程池(threadpool).缓存(cache).对

java/android 设计模式学习笔记(13)---享元模式

这篇我们来介绍一下享元模式(Flyweight Pattern),Flyweight 代表轻量级的意思,享元模式是对象池的一种实现.享元模式用来尽可能减少内存使用量,它适合用于可能存在大量重复对象的场景,缓存可共享的对象,来达到对象共享和避免创建过多对象的效果,这样一来就可以提升性能,避免内存移除和频繁 GC 等. 享元模式的一个经典使用案例是文本系统中图形显示所用的数据结构,一个文本系统能够显示的字符种类就是那么几十上百个,那么就定义这么些基础字符对象,存储每个字符的显示外形和其他的格式化数据

java/android 设计模式学习笔记(5)---对象池模式

这次要介绍一下对象池模式(Object Pool Pattern),这个模式为常见 23 种设计模式之外的设计模式,介绍的初衷主要是在平时的 android 开发中经常会看到,比如 ThreadPool 和 MessagePool 等. 在 java 中,所有对象的内存由虚拟机管理,所以在某些情况下,需要频繁创建一些生命周期很短使用完之后就可以立即销毁,但是数量很大的对象集合,那么此时 GC 的次数必然会增加,这时候为了减小系统 GC 的压力,对象池模式就很适用了.对象池模式也是创建型模式之一,

java/android 设计模式学习笔记(14)---外观模式

这篇博客来介绍外观模式(Facade Pattern),外观模式也称为门面模式,它在开发过程中运用频率非常高,尤其是第三方 SDK 基本很大概率都会使用外观模式.通过一个外观类使得整个子系统只有一个统一的高层的接口,这样能够降低用户的使用成本,也对用户屏蔽了很多实现细节.当然,在我们的开发过程中,外观模式也是我们封装 API 的常用手段,例如网络模块.ImageLoader 模块等.其实我们在开发过程中可能已经使用过很多次外观模式,只是没有从理论层面去了解它. 转载请注明出处:http://bl

java/android 设计模式学习笔记(7)---装饰者模式

这篇将会介绍装饰者模式(Decorator Pattern),装饰者模式也称为包装模式(Wrapper Pattern),结构型模式之一,其使用一种对客户端透明的方式来动态的扩展对象的功能,同时它也是继承关系的一种替代方案之一,但比继承更加灵活.在现实生活中也可以看到很多装饰者模式的例子,或者可以大胆的说装饰者模式无处不在,就拿一件东西来说,可以给它披上无数层不一样的外壳,但是这件东西还是这件东西,外壳不过是用来扩展这个东西的功能而已,这就是装饰者模式,装饰者的这个角色也许各不相同但是被装饰的对

java/android 设计模式学习笔记(12)---组合模式

这篇我们来介绍一下组合模式(Composite Pattern),它也称为部分整体模式(Part-Whole Pattern),结构型模式之一.组合模式比较简单,它将一组相似的对象看作一个对象处理,并根据一个树状结构来组合对象,然后提供一个统一的方法去访问相应的对象,以此忽略掉对象与对象集合之间的差别.这个最典型的例子就是数据结构中的树了,如果一个节点有子节点,那么它就是枝干节点,如果没有子节点,那么它就是叶子节点,那么怎么把枝干节点和叶子节点统一当作一种对象处理呢?这就需要用到组合模式了. 转

java/android 设计模式学习笔记(9)---代理模式

这篇博客我们来介绍一下代理模式(Proxy Pattern),代理模式也成为委托模式,是一个非常重要的设计模式,不少设计模式也都会有代理模式的影子.代理在我们日常生活中也很常见,比如上网时连接的代理服务器地址,更比如我们平时租房子,将找房子的过程代理给中介等等,都是代理模式在日常生活中的使用例子. 代理模式中的代理对象能够连接任何事物:一个网络连接,一个占用很多内存的大对象,一个文件,或者是一些复制起来代价很高甚至根本不可能复制的一些资源.总之,代理是一个由客户端调用去访问幕后真正服务的包装对象