zoukankan      html  css  js  c++  java
  • 设计原则:不要为了复用而使用继承

    背景

    今天上午和以为朋友聊了一个设计问题:如何消除仓库相关的单据的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,鸭子类型天生具备泛型的特点,比泛型强大,元编程更是牛叉(本文没有体现)。

    备注

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

  • 相关阅读:
    hdu 1455 N个短木棒 拼成长度相等的几根长木棒 (DFS)
    hdu 1181 以b开头m结尾的咒语 (DFS)
    hdu 1258 从n个数中找和为t的组合 (DFS)
    hdu 4707 仓鼠 记录深度 (BFS)
    LightOJ 1140 How Many Zeroes? (数位DP)
    HDU 3709 Balanced Number (数位DP)
    HDU 3652 B-number (数位DP)
    HDU 5900 QSC and Master (区间DP)
    HDU 5901 Count primes (模板题)
    CodeForces 712C Memory and De-Evolution (贪心+暴力)
  • 原文地址:https://www.cnblogs.com/happyframework/p/3277026.html
Copyright © 2011-2022 走看看