设计模式~三种工厂模式

234 阅读7分钟

一、简单工厂模式

1. 定义

  • 简单工厂模式又名静态方法工厂模式,是由一个工厂对象决定创建出哪一种产品类的实例

2. UML类图

image.png

  • 抽象产品类(IProduct):创建的所有对象的父类,它负责描述所有实例所共有的公共接口
  • 产品工厂类(SimpleFactory):负责生产各种具体的产品,不关心产品产生的过程,只关心要生产的产品的类型
  • 具体产品类(Product):具体的产品,封装了产品建造的过程以及使用的教程

3. 使用场景

  • 工厂类负责创建的对象比较少。
  • 客户只知道传入工厂类的参数,对于如何创建对象(逻辑)不关心

4. 简单实现

  • 需求:不同手机型号需要使用不同的推送,可以集成小米、华为、极光推送然后创建一个工厂类根据手机型号来使用不同的推送
  • 1、定义推送调用的接口
/**
 * 定义一个通用的push推送接口
 * @author LTP  2021/11/10
 */
interface IPush {
    /** 定义一个抽象的push方法 */
    fun push()
}
  • 2、封装不同的推送调用方法
/**
* 具体产品类:小米推送具体实现
* @author LTP  2021/11/10
*/
class MiPush : IPush {

   override fun push() {
       println("小米手机使用小米推送")
   }
}
/**
 * 具体产品类:华为推送具体实现
 * @author LTP  2021/11/10
 */
class HuaWeiPush : IPush {

    override fun push() {
        println("华为手机使用华为推送")
    }
}
/**
 * 具体产品类:极光推送具体实现
 * @author LTP  2021/11/10
 */
class JiGuangPush : IPush {

    override fun push() {
        println("其他手机使用极光推送")
    }
}
  • 3、创建一个push工厂
/**
 * 推送工厂类
 * @author LTP  2021/11/10
 */
object PushFactory {

    /**
     * 根据具体的手机类型使用具体的推送服务
     *
     * @param type 推送类型
     * @return Push 具体的推送类型
     */
    fun createPush(type: String): IPush {
        return when (type) {
            "xiaoMi" -> MiPush()
            "huaWei" -> HuaWeiPush()
            else -> JiGuangPush()
        }
    }
}
  • 4、客户端调用
/**
 * 具体调用
 *
 * @author LTP  2021/11/10
 */
class CreatePush {

    companion object {
        @JvmStatic
        fun main(args: Array<String>) {
            PushFactory.createPush("xiaoMi").push()
            PushFactory.createPush("huaWei").push()
        }
    }
}
运行结果:
小米手机使用小米推送
华为手机使用华为推送

5. 优缺点

  • 优点:用户根据参数获得对应的类实例,避免了直接实例化类,降低了耦合性节
  • 缺点:类型在编译期间已经被确定,增加新类型需要修改工厂,违背了开放封闭原则(ASD) ;需要事先知道所有要生成的类型,当子类过多或者子类层次过多时不适合使用

二、工厂方法模式

1. 定义

  • 定义一个用于创建对象的接口,让子类决定实例化哪个类

2. UML类图

image.png

  • Product:抽象产品类。
  • ConcreteProduct:具体产品类,实现Product接口。
  • Factory:抽象工厂类,该方法返回一个Product类型的对象。
  • ConcreteFactory:具体工厂类,返回ConcreteProduct实例

3. 使用场景

  • 在任何需要生成复杂对象的地方,都可以使用工厂方法模式
  • 复杂对象适合使用工厂模式,用new就可以创建的简单对象无需使用工厂方法模式

4. 简单实现

  • 新需求:在原来基础上,oppo手机需加入oppo推送,而且后面可能还会有更多手机厂商的推送...
  • 1、在原来的基础上加一层抽象工厂类
  /**
   * 推送抽象工厂类
   * @author LTP  2021/11/10
   */
  abstract class AbsPushFactory {
      abstract fun <T : IPush> createPush(clazz: Class<T>): T
  }
  • 2、原来的工厂类继承抽象工厂类利用反射来初始化各产品对象
 /**
  * @author LTP  2021/11/10
  */
 object PushFactory : AbsPushFactory() {

    override fun <T : IPush> createPush(clazz: Class<T>): T {
        return Class.forName(clazz.name).getDeclaredConstructor().newInstance() as T
    }
}
  • 3、这里跟以前小米华为一样,添加oppo推送的具体产品类......
/**
 * 具体产品类:Oppo推送具体实现
 * @author LTP  2021/11/10
 */
class OppoPush : IPush {

    override fun push() {
        println("oppo手机使用oppo推送")
    }
}
  • 4、客户端调用
/**
 * 具体调用
 * 
 * @author LTP  2021/11/10
 */
class CreatePush {

    companion object {
        @JvmStatic
        fun main(args: Array<String>) {
            PushFactory.createPush(MiPush::class.java).push()
            PushFactory.createPush(HuaWeiPush::class.java).push()
            PushFactory.createPush(OppoPush::class.java).push()
        }
    }
}
运行结果:
小米手机使用小米推送
华为手机使用华为推送
oppo手机使用oppo推送

5. 源码中的使用场景

  • Activity中的onCreate方法通过setContentView设置布局
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new LinearLayout(this));
    }
}
  • Service中的onBind方法
public class MyService extends Service {

    @Override
    public IBinder onBind(Intent intent) {
       return new Binder();
    }
}

5. 优缺点

  • 优点:可以自由新增更多的产品线而不破坏开放封闭原则
  • 缺点:使用反射,多多少少影响性能

三、抽象工厂模式

1. 定义

  • 为创建一组相关或者相互依赖的对象提供一个接口,而无需指定它们的具体类

2. UML类图

image.png

  • AbstractFactory:抽象工厂,它声明了用来创建不同产品的方法
  • ConcreteFactory:具体工厂,实现抽象工厂中定义的创建产品的方法,生成一组具体产品
  • AbstractProduct:抽象产品,为每种产品声明业务方法
  • ConcreteProduct:具体产品,定义具体工厂生产的具体产品,并实现抽象产品中定义的业务方法

3. 使用场景

  • 一个系统不依赖于产品线实例如何被创建、组合和表达的细节
  • 系统中有多于一个的产品线,而每次只使用其中某一产品线
  • 一个产品线(或是一组没有任何关系的对象)拥有相同的约束

4. 简单实现

  • 新需求:添加针对向不同的手机厂商发短信
  • 1、添加一个Isend接口
/**
 * 定义一个通用的发送短信接口
 * @author LTP  2021/11/10
 */
interface ISend {
    /** 定义一个抽象的send方法 */
    fun send()
}
  • 2、华为小米分别添加对Isend的实现
/**
 * 具体产品类:小米短信具体实现(华为代码同理已省略)
 * @author LTP  2021/11/10
 */
class MiSend : ISend {

    override fun send() {
        println("小米手机发送小米短信")
    }
}
  • 3、添加包含push和send的抽象工厂
/**
 * 推送和发短信的抽象工厂类
 * @author LTP  2021/11/10
 */
abstract class AbsPushSendFactory {

    abstract fun createPush(): IPush
    abstract fun createSend(): ISend
}
  • 4、分别实现小米工厂和华为工厂(华为工厂代码类似已省略)
/**
 * 小米工厂类
 *
 * @author LTP  2021/11/10
 */
class MiFactory : AbsPushSendFactory() {

    override fun createPush(): IPush {
        return MiPush()
    }

    override fun createSend(): ISend {
        return MiSend()
    }
}
  • 5、客户端调用
/**
 * 具体调用
 *
 * @author LTP  2021/11/10
 */
class CreatePushSend {

    companion object {
        @JvmStatic
        fun main(args: Array<String>) {
            // 小米工厂
            val miFactory = MiFactory()
            miFactory.createPush().push()
            miFactory.createSend().send()

            // 华为工厂
            val huaWeiFactory = HuaWeiFactory()
            huaWeiFactory.createPush().push()
            huaWeiFactory.createSend().send()
        }
    }
}
运行结果:
小米手机使用小米推送
小米手机发送小米短信
华为手机使用华为推送
华为手机发送华为短信

5. 优缺点

  • 优点:具体类的创建实例过程与客户端分离,客户端通过工厂的抽象接口操纵实例,客户端并不知道具体的实现是谁
  • 缺点:增加新的产品族则也需要修改抽象工厂和所有的具体工厂

四. 三种工厂模式对比

  • 简单工厂模式:产品线固定;工厂类,无抽象工厂类
  • 工厂方法模式:单一产品线可延伸产品(添加产品,只需添加IProduct的新产品实现类即可);一个抽象工厂类,所有产品共用一个工厂类
  • 抽象工厂模式:固定多产品线可延伸产品(添加产品,需添加IProduct的新产品实现类以及新产品的工厂类);一个抽象工厂类,每一个产品都有一个工厂类