背景

今天上午和以为朋友聊了一个设计问题:如何消除仓库相关的单据的Repository中的重复逻辑?如:入库单Repository和出库单Repository之间的重复。可以有很多方式消除重复,在不同级别消除重复,如:继承、组合、掺入、帮助类、帮助方法。本文只说出我的观点:不要为了复用而使用继承。

为什么要得出这个结论:在单实现继承模型下,你复用了一个基类的实现,就不能复用其它基类的实现了,接口继承 + 扩展类型(Mixin)可以很好的解决这个问题。

设计的演化

下面我会演示:待重构的重复代码-》用继承消除重复-》用扩展类(Mixin)消除重复-》Ruby的鸭子类型 + Mixin的实现(元编程可以更牛叉,有机会再说)。

待重构的代码

注意:出库单仓储和入库单仓储的“根据编号获取单据”重复了。

类图

代码

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 using System.Linq.Expressions;
 7
 8 namespace CSharpStudy.MixinStudy.V1
 9 {
10     class Aggregate { }
11
12     interface IRepository<T> where T : Aggregate
13     {
14         IEnumerable<T> Where(Expression<Func<T, bool>> condition);
15     }
16
17     class Repository<T> : IRepository<T>
18         where T : Aggregate
19     {
20         public IEnumerable<T> Where(Expression<Func<T, bool>> condition)
21         {
22             throw new NotImplementedException();
23         }
24     }
25
26     class 入库单 : Aggregate
27     {
28         public string 单据编号 { get; set; }
29     }
30
31     class 出库单 : Aggregate
32     {

用继承消除重复

当我看到上面的重复代码的时候,第一印象是引入两个基类:仓库单据基类和仓库单据仓储基类。

类图

代码

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 using System.Linq.Expressions;
 7
 8 namespace CSharpStudy.MixinStudy.V2
 9 {
10     class Aggregate { }
11
12     interface IRepository<T> where T : Aggregate
13     {
14         IEnumerable<T> Where(Expression<Func<T, bool>> condition);
15     }
16
17     class Repository<T> : IRepository<T>
18         where T : Aggregate
19     {
20         public IEnumerable<T> Where(Expression<Func<T, bool>> condition)
21         {
22             throw new NotImplementedException();
23         }
24     }
25
26     class 仓库单据基类 : Aggregate
27     {
28         public string 单据编号 { get; set; }
29     }
30
31     class 入库单 : 仓库单据基类 { }
32
33     class 出库单 : 仓库单据基类 { }
34
35     class 仓库单据仓储基类<T> : Repository<T>
36         where T : 仓库单据基类
37     {
38         public IEnumerable<T> 根据编号获取单据(string 单据编号)
39         {
40             return this.Where(x => x.单据编号 == 单据编号);
41         }
42     }
43
44     class 入库单仓储 : 仓库单据仓储基类<入库单> { }
45
46     class 出库单仓储 : 仓库单据仓储基类<出库单> { }
47 }

用扩展类(Mixin)消除重复

我对了吗?没有多态,只是为了复用就引入继承,是否合理呢?

类图

代码

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 using System.Linq.Expressions;
 7
 8 namespace CSharpStudy.MixinStudy.V3
 9 {
10     class Aggregate { }
11
12     interface IRepository<T> where T : Aggregate
13     {
14         IEnumerable<T> Where(Expression<Func<T, bool>> condition);
15     }
16
17     class Repository<T> : IRepository<T>
18         where T : Aggregate
19     {
20         public IEnumerable<T> Where(Expression<Func<T, bool>> condition)
21         {
22             throw new NotImplementedException();
23         }
24     }
25
26     class 仓库单据基类 : Aggregate
27     {
28         public string 单据编号 { get; set; }
29     }
30
31     class 入库单 : 仓库单据基类 { }
32
33     class 出库单 : 仓库单据基类 { }
34
35     static class 仓库单据基类仓储扩展
36     {
37         public static IEnumerable<T> 根据编号获取单据<T>(this IRepository<T> that, string 单据编号)
38             where T : 仓库单据基类
39         {
40             return that.Where(x => x.单据编号 == 单据编号);
41         }
42     }
43 }

Ruby的鸭子类型 + Mixin的实现

代码

 1 # coding: utf-8
 2
 3 class Aggregate
 4 end
 5
 6 class Repository
 7     def Where(condition)
 8     end
 9 end
10
11 class C仓库单据基类 < Repository
12     attr_accessor :单据编号
13 end
14
15 class C入库单 < C仓库单据基类
16 end
17
18 class C出库单 < C仓库单据基类
19 end
20
21 module C仓库单据基类仓储扩展
22     def 根据编号获取单据(单据编号)
23         return self.Where({:单据编号 => 单据编号})
24     end
25 end
26
27 class C入库单仓储 < Repository
28     include C仓库单据基类仓储扩展
29 end
30
31 class C出库单仓储 < Repository
32     include C仓库单据基类仓储扩展
33 end

Ruby正统的支持了Mixin,鸭子类型天生具备泛型的特点,比泛型强大,元编程更是牛叉(本文没有体现)。

备注

做一件事如果只有一个选择,就说明有问题了,多思考几个方案,折中后考虑一个方案。

设计原则:不要为了复用而使用继承相关推荐

  1. 七大设计原则之合成复用原则

    一.合成复用原则介绍 合成复用原则是指尽量使用合成/聚合的方式,而不是使用继承.类图介绍如下: 二.依赖关系.组合关系.聚合关系介绍 2.1依赖关系(Dependency) 只要是在类中用到了对方,那 ...

  2. 面向对象设计原则之合成复用原则

    合成复用原则又称为组合/聚合复用原则(Composition/Aggregate Reuse Principle, CARP),其定义如下: 合成复用原则(Composite Reuse Princi ...

  3. 设计原则之合成复用原则

    合成复用原则是指: 尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现. 通常类的复用分为继承复用和合成复用两种. 继承复用虽然有简单和易实现的优点,但它也存在以下缺点: 1. 继承 ...

  4. 设计模式之美总结(设计原则篇)

    title: 设计模式之美总结(设计原则篇) date: 2022-10-27 17:31:42 tags: 设计模式 categories: 技术书籍及课程 cover: https://cover ...

  5. 设计原则:里式替换原则(LSP)

    系列文章 设计原则:单一职责(SRP) 设计原则:开闭原则(OCP) 设计原则:里式替换原则(LSP) 设计原则:接口隔离原则(ISP) 设计原则:依赖倒置原则(DIP) 何谓高质量代码? 理解RES ...

  6. 设计原则与思想:设计原则12讲

    文章目录 设计原则与思想:设计原则(12讲) 理论一:对于单一职责原则,如何判定某个类的职责是否够"单一"? 如何理解单一职责原则(SRP)? 如何判断类的职责是否足够单一? 类的 ...

  7. 设计模式-02.经典设计原则-第一节-单一职责原则,开闭原则,里式替换,接口隔离【万字长文系列】

    文章目录 设计模式经典设计原则-第一节 单一职责原则(SRP) 如何理解单一职责原则? 如何判断类的职责是否足够单一? 类的职责是否设计得越单一越好? 开闭原则(OCP) 如何理解"对扩展开 ...

  8. Go设计模式(3)-设计原则

    上一篇文章Go设计模式(2)-面向对象分析与设计里讲过,做设计最重要的是保留合适的扩展点.如何才能设计出合适的扩展点呢? 这篇文章会讲解一下经典的设计原则.这些设计原则大家可能都听过,但可能没有想过为 ...

  9. 004.设计原则与思想:设计原则

    设计原则 一.理论一:对于单一职责原则,如何判定某个类的职责是否够"单一"? 1. 如何理解单一职责原则(SRP)? 二. 如何做到"对扩展开放.修改关闭"?扩 ...

  10. 设计模式之软件设计原则篇

    3.软件设计原则 本文的内容绝大部分借鉴了https://www.jianshu.com/u/cc272db15285的内容,感兴趣的小伙伴可以进入其简书浏览更细的内容,讲的非常好. 在软件开发中,为 ...

最新文章

  1. IEEE发布人工智能伦理标准,确保人类不受影响
  2. 鸡年除夕全天微信红包收发量达142亿个增长75.7%
  3. CentOS VS Ubuntu,谁才是更好的 Linux 版本?
  4. Microsoft SQL Server 2005 查询分页
  5. SpringBoot与jackson.databind兼容报错问题
  6. 20多年的朋友如兄弟,没有矛盾4个人如今都绝交了什么原因?
  7. Hyperledger Fabric 节点类型Commiter、Endorser、Leader、Anchor
  8. Javashop 7.0 增加小程序支付(二次开发)
  9. 关于Chrome浏览器书签被更改的分析
  10. 你必须牢记的Win7快捷键
  11. word中,解决插入形状后固定位置不变。
  12. TearDrop拒绝服务攻击
  13. grafana 获取禅道bug执行sql语句的问题
  14. 微信公众号无法获得用户列表中的昵称和用户头像URL
  15. JKD+Tomcat+Eclipse基础配置
  16. 关于出生日期、邮箱、上传下载操作的元素点分析
  17. 【安装系统】windows10、ubuntu16.04系统安装
  18. gcc -I -i -L -l 参数区别 / -l(静态库/动态库)
  19. Ubuntu 18.04 连接有线网络
  20. 解决JAVA键盘监听的延迟现象以及八个方向的运动

热门文章

  1. LeetCode15:三数之和(转载思路)
  2. python日志,支持彩色打印和文件大小切片写入和写入mongodb
  3. rabbitmq延迟队列相关
  4. 【一周读书】哲学家,你们都干了些什么?
  5. 需求阶段如何书写Use Case
  6. html5怎么圆圈怎么打开,HTML5动感圆圈
  7. vrp 节约算法 c++_数据结构和算法(Golang实现)(8.1)基础知识-前言
  8. 项目配置不当引发了数据泄露,人已裂开!!
  9. 爬取了BOSS直聘、拉勾等近1000+招聘需求,总结出3年+Java开发的高频技术需求
  10. Java的transient关键字