【问题标题】:C# Interface: extracting additional public property value (not part of interface) from a concrete classC# 接口:从具体类中提取额外的公共属性值(不是接口的一部分)
【发布时间】:2020-03-03 14:21:02
【问题描述】:

开头的代码示例:

  internal class ClubHouse : ILeasable
  {
      public int Id { get; set; }

      public int AreaInSquareFeet { get; set; }
  }
 public class Parking : ILeasable
 {
    public int Id { get; set; }
    public int CarCapacity { get; set; }
 }
  internal interface ILeasable
  {
    int Id { get; set; }
  }
  class LeasableRepository
  {
    private List<ILeasable> _leasable = new List<ILeasable>()
    {
        new ClubHouse() {Id = 208, AreaInSquareFeet = 7500 },
        new ShowRoom(){ Id = 202, AreaInSquareFeet = 4000 },
        new Parking() {Id = 504, CarCapacity = 4},
    };

    private Dictionary<int, ILeasable> _leasableDictionary = new Dictionary<int, ILeasable>();

    public LeasableRepository()
    {
        _leasableDictionary = _leasable.ToDictionary(x => x.Id, x => x);
    }

    public ILeasable GetLeasable(int id)
    {
        if (_leasableDictionary.ContainsKey(id)) return _leasableDictionary[id];
        return null;
    }
  }

  public class ChargeCalculatingFacade
  {
    LeasableRepository leasableRepository = new LeasableRepository();
    public void ShowLeasingCharges(int id)
    {
        var leasable = leasableRepository.GetLeasable(id);
        var leasingCharge = GetLeasingCharges(leasable);

    }

    private int GetLeasingCharges(ILeasable leasable)
    {

        // This is not possible as I can't be sure that leasable is ClubHouse
        var property = (ClubHouse) leasable;
        var areaInSquareFeet = property.AreaInSquareFeet;

        return areaInSquareFeet * 10;
    }
  }

现在,在 ChargeCalculatingFacade 类中,在方法 ShowLeasingCharges(int id) 中,基于 id,我调用了 GetLeasable(int id),它返回 ILeasable 的实现之一。但是它会作为接口 ILeasable 返回。

我将该 ILeasable 传递给私有方法 GetLeasingCharges(leasable) 以根据 AreaInSquareFeet 计算租赁费用。

现在,leasable 参数只是 ILeasable,它只有“Id”属性可用。现在如何识别作为参数传递的具体类实现,我可以将它转换为像这样得到 AreaInSquareFeet

        var property = (ClubHouse) leasable;
        var areaInSquareFeet = property.AreaInSquareFeet;

但上面的代码是不可行的,因为我不确定可租用的是否是 ClubHouse,因为它只是根据 ID 从字典中选择可租用的。

所有类都没有相同的附加属性。例如,Parking 具有附加属性“CarCapacity”。我有 10 个这样的类,现在不能放 10 个 if 逻辑来检查接口是否是必需的类类型。

我想知道一些设计模式或一些 SOLID 原则是否可以简化设计。

我有以下问题:

  1. 在这种情况下如何获取 areaInSquareFeet
  2. 拥有一个具有少量方法和属性的接口,并在 concreate 类中再次具有其他公共方法或属性,这是一种好习惯吗?

注意:我不想使用反射。如果没有反射是不可能的,我想改变一个设计。有什么设计建议吗?在这种情况下可以使用任何设计模式吗?'

谢谢。 三田

【问题讨论】:

    标签: c# interface


    【解决方案1】:

    A. ILeasable.GetLeasingCharges

    如果GetLeasingCharges 仅依赖于对象已有的数据,我可能会认为将GetLeasingCharges 作为ILeasable 的一部分可能是更好的选择。

    internal interface ILeasable
    {
      int Id { get; set; }
      int GetLeasingCharges();
    }
    
    
    internal class ClubHouse : ILeasable
    {
      public int Id { get; set; }
    
      public int AreaInSquareFeet { get; set; }
    
      public int GetLeasingCharges() => AreaInSquareFeet * 10;
    }
    
    internal class ClubHouse : ILeasable
    {
      public int Id { get; set; }
    
      public int CarCapcity{ get; set; }
    
      public int GetLeasingCharges() => CarCapcity * 15;
    }
    
    

    B. GetLeasingCharges 不属于 ILeasable

    来自C#7.0,您可以使用pattern matching 处理此类情况。

    public static int GetLeasingCharges(ILeasable leasable)
    {
        // From c#7.0
        switch (leasable)
        {
            case ClubHouse c:
                return c.AreaInSquareFeet * 10;
            case ShowRoom s:
                return s.AreaInSquareFeet * 12;
            case Parking p:
                throw new ArgumentException(
                    message: "Parkings cannot be leased!",
                    paramName: nameof(leasable));
            default:
                throw new ArgumentException(
                    message: "Unknown type",
                    paramName: nameof(leasable));
        }
    }
    

    在C#7.0 之前,您可以使用if。

    if (leasable is ClubHouse)
    {
        var c = (ClubHouse)leasable;
        return c.AreaInSquareFeet * 10;
    } 
    else if (leasable is ShowRoom)
    {
        var c = (ShowRoom)leasable;
        return s.AreaInSquareFeet * 12;
    }
    else if(leasable is Parking)
    {
        throw new ArgumentException(
             message: "Parkings cannot be leased!",
             paramName: nameof(leasable));
    }
    else 
    {
        throw new ArgumentException(
            message: "Unknown type",
            paramName: nameof(leasable));
    }
    

    【讨论】:

    • 有没有办法避免这么多 if-else。我想避免打破开闭原则。
    • @app A 计划怎么样?
    【解决方案2】:

    我非常同意@tymtam 的做法。您也可以在替代方案中使用抽象类。

    public abstract class ChargeCalculatingFacadeBase<T> where T : ILeasable
    {
        LeasableRepository leasableRepository = new LeasableRepository();
        public ILeasable leasable;
        public void ShowLeasingCharges(int id)
        {
            leasable = leasableRepository.GetLeasable(id);
            var leasingCharge = GetLeasingCharges((T)leasable);
    
        }
        public abstract int GetLeasingCharges(T leasable);
    }
    public class ChargeCalculatingFacade : ChargeCalculatingFacadeBase<ClubHouse>
    {
        public override int GetLeasingCharges(ClubHouse leasable)
        {
            var property = leasable;
            var areaInSquareFeet = property.AreaInSquareFeet;
    
            return areaInSquareFeet * 10;
        }
    }
    

    【讨论】:

      【解决方案3】:

      虽然这可能有点矫枉过正,但在与您类似的情况下,我曾多次使用伪 DSL 方法。 也就是说,我先想出一种语言来表达我的意图,然后再去实现它。

      你需要什么?以可读的方式表达计算的能力。让我们这样做:假设你有一个类

      public class LeaseCalculator
      {
          public int CalculateLease(int id) ...
      

      我想这样初始化它:

          var builder = new LeaseCalculatorBuilder();
      
          LeaseCalculator calculator = builder
              .On<ClubHouse>(house => house.AreaInSquareFeet)
              .On<Parking>(park => park.CarCapacity)
              .On<ShowRoom>(room => room.AreaInSquareFeet)
              .Build(leasableRepository);
      

      意图明确吗?我相信是这样。如果我们有会所,我们会做第一件事;用于停车,其他东西等。

      现在,执行。我也可以一步一步走,但长话短说:

      public class LeaseCalculatorBuilder
      {
          internal Dictionary<Type, Func<ILeasable, int>> Calculations { get; } = new Dictionary<Type, Func<ILeasable, int>>();
      
          internal LeaseCalculatorBuilder On<T>(Func<T, int> calculation) where T : class, ILeasable
          {
              Calculations.Add(typeof(T), (ILeasable c) => calculation((T)c));
              return this;
          }
      
          internal LeaseCalculator Build(LeasableRepository leasableRepository)
          {
              return new LeaseCalculator(leasableRepository, this);
          }
      }
      
      public class LeaseCalculator
      {
          private readonly Dictionary<Type, Func<ILeasable, int>> _calculations;
          private readonly LeasableRepository _leasableRepository;
      
          internal LeaseCalculator(LeasableRepository leasableRepository, LeaseCalculatorBuilder builder)
          {
              _leasableRepository = leasableRepository;
              _calculations = builder.Calculations;
          }
      
          public int CalculateLease(int id)
          {
              ILeasable property = _leasableRepository.GetLeasable(id);
              Type type = property.GetType();
              if (_calculations.TryGetValue(type, out var calculation))
              {
                  return calculation(property);
              }
              throw new Exception("Unexpected type, please extend the calculator");
          }
      }
      

      最后,一个默认创建者:

      public static class DefaultLeaseCalculator
      {
          internal static LeaseCalculator Build(LeasableRepository leasableRepository)
          {
              var builder = new LeaseCalculatorBuilder();
      
              LeaseCalculator calculator = builder
                  .On<ClubHouse>(house => house.AreaInSquareFeet)
                  .On<Parking>(park => park.CarCapacity)
                  .On<ShowRoom>(room => room.AreaInSquareFeet)
                  .Build(leasableRepository);
      
              return calculator;
          }
      }
      

      干净吗?

      【讨论】:

        猜你喜欢
        • 2017-12-29
        • 2015-01-28
        • 2013-09-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多