构建创造者类的继承层次

特点:

  • 会产生不必要的子类化
  • 结构层次与产品类的层次结构相同


使用场景:

大型项目,需要生成大量对象时,**工厂模式+抽象工厂模式+原型模式+服务定位器** 组合使用。

Ply.png

问题:

假设一个支付系统中,客户要求当前系统要使用微信支付,其中一个是 **WechatAppPly** 对象,但客户强调之后可能会扩展支付业务,要支持:支付宝、银联等支付
针对目前已知情况,可以创建以下两个抽象类:

  • CommonsManager
  • AppPly

AppPly 负责管理产品,而 CommonsManager 负责创建具体的支付产品。
如果我要得到具体的 AppPly 呢?我可以把生成 AppPly具体子类的推给客户端(调用方)来解决,但只是推迟了问题的发生,现在我想要彻底解决这个问题,接下来我直接在 CommonsManager 类中直接实例化一个WechatAppPly

  1. abstract AppPly
  2. {
  3. public function ply(): void;
  4. }
  5. // 具体产品
  6. class WechatAppPly extends AppPly
  7. {
  8. public function ply(): void
  9. {
  10. // 微信支付逻辑
  11. }
  12. }
  1. abstract CommonsManager
  2. {
  3. public static function getAppPly(): AppPly
  4. {
  5. return new WechatAppPly();
  6. }
  7. }

CommonsManager负责生成 WechatAppPly类,但是现在,客户要求新增支付宝支付功能,我简单的添加一个条件语句到 CommonsManager::getAppPly()方法中即可。接下来我修改 CommonsManager类,他它可以同时处理 WchatAppPlyALiAppPly

  1. class ALiAppPly extends AppPly
  2. {
  3. public function ply(): void
  4. {
  5. // 支付宝支付逻辑
  6. }
  7. }
  1. abstract CommonsManager
  2. {
  3. public const WECHAT = 'wechat';
  4. public const ALi = 'ali';
  5. private ?string $mode;
  6. public function __construct(string $mode)
  7. {
  8. $this->mode = $mode;
  9. }
  10. public function getAppPly(): AppPly
  11. {
  12. return match($this->model){
  13. 'wechat' => return new WechatAppPly();
  14. default => new ALiAppPly();
  15. }
  16. }
  17. }
  18. // 调用过程
  19. $com = new CommonsManager(CommonsManager::WECHAT);
  20. // 执行支付方法
  21. $com->getAppPly()->ply(); // wechat ply
  22. $com = new CommonsManager(CommonsManager::ALi);
  23. $com->getAppPly()->ply();// ali ply

这种方式仍然存在一个小问题。条件语句有时候被称为 “代码异味”,如果这时候,如果这时候,客户要求在支付前和支付后要记录当前支付功能的日志。那意味着同样的我要在 CommonsManager 类中提供与生成支付类相同的条件判断语句来创建对应的记录器

  1. abstract CommonsManager
  2. {
  3. public const WECHAT = 'wechat';
  4. public const ALi = 'ali';
  5. private ?string $mode;
  6. public function __construct(string $mode)
  7. {
  8. $this->mode = $mode;
  9. }
  10. public function getAppPly(): AppPly
  11. {
  12. return match($this->model){
  13. 'wechat' => return new WechatAppPly();
  14. default => new ALiAppPly();
  15. }
  16. }
  17. public function recordBeforePayLog()
  18. {
  19. if ($this->model === 'wechat') {
  20. // 记录微信支付开始前记录
  21. }else {
  22. // 记录支付支付开始前记录
  23. }
  24. }

现在问题已经很暴露得很明显了,特别是还需要再添加一个 recordAfterPayLog() 方法时,这种检查会越来越多。这些条件语句可以被多态所替代。可以为每种支付编写一个 CommonsManager 的子类,让它们各自实现 getAppPly() 方法。

Factory.png

以下是简化的示例代码:

  1. abstract AppPly
  2. {
  3. abstract public function ply(): void;
  4. }
  5. // 具体产品实现
  6. class WechatAppPly extends AppPly
  7. {
  8. public function ply(): void
  9. {
  10. // 微信支付逻辑
  11. }
  12. }
  1. abstract CommonsManager
  2. {
  3. abstract public function getAppPly(): AppPly;
  4. abstract public function recordBeforePlyLog(): void;
  5. abstract public function recordAfterPlyLog(): void;
  6. }
  7. // 产品创建者
  8. class WechatAppPlyCommonManager extends CommonsManager
  9. {
  10. public function getAppPly(): AppPly
  11. {
  12. return new WechatAppPly();
  13. }
  14. public function recordBeforePlyLog()
  15. {
  16. // 记录支付前日志
  17. }
  18. public function recordAfterPlyLog()
  19. {
  20. // 记录支付后日志
  21. }
  22. }
  23. // 调用
  24. $mgr = new WechatAppPlyCommonManager();
  25. $mgr->recordBeforePlyLog();
  26. $mgr->getAppPly()->ply();
  27. $mgr->recordAfterPlyLog();

即使现在要求实现 AliAppPly 时,只需要为抽象类型实现新的子类即可,修改后包含 AliAppPly 类类图如下所示:
1.png

总结:

工场模式使用起来还是有缺陷的,前面讲过,虽然它规避了重复的条件语句,分离了创建者与产品类之间的耦合,但是它衍生出了另一个问题,它的层次结构变得和产品类的层次结构相同。形成了另一种特殊的代码重复,并且产生了不必要的子类。