Интерфейс C #: извлечение дополнительного значения открытого свойства (не части интерфейса) из конкретного класса - PullRequest
0 голосов
/ 07 ноября 2019

Пример кода для начала:

  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;
    }
  }

Теперь в классе ChargeCalculationFacade в методе ShowLeasingCharges (int id), основанном на идентификаторе, я вызвал GetLeasable (int id), который возвращает один изреализация ILeasable. Однако он возвращается в качестве интерфейса ILeasable.

Я передаю этот ILeasable частному методу GetLeasingCharges (leasable) для расчета арендных платежей на основе AreaInSquareFeet.

Теперь параметр leasable является просто ILeasable,который имеет только свойство "Id". Теперь, как определить, какая реализация класса concreat передается в качестве параметра, я могу привести его к типу AreaInSquareFeet следующим образом:

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

Но приведенный выше код невозможен, так как я не уверен, что leasable это ClubHouse, так какпросто выбирает возможность аренды из словаря на основе идентификатора.

Все классы не имеют такого же дополнительного свойства. Например, у парковки есть дополнительное свойство как «CarCapacity». У меня есть 10 таких классов, теперь я не могу поставить 10, если логика проверяет, имеет ли интерфейс требуемый тип класса.

Интересно, может ли какой-либо шаблон проектирования или какой-либо принцип SOLID упростить проект.

У меня есть следующие вопросы:

  1. Как я могу получить areaInSquareFeet в таком случае
  2. Это хорошая практика - иметь интерфейс с несколькими методами и свойствами и снова иметь дополнительные открытые методы илисвойства в конкретном классе.

Примечание: я не хочу использовать отражение. Я хотел бы изменить дизайн в случае, если без отражения невозможно. Какие-нибудь дизайнерские предложения? В таком сценарии можно использовать любой шаблон дизайна? '

Спасибо. Mita

Ответы [ 4 ]

1 голос
/ 07 ноября 2019

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, вы можете использовать сопоставление с образцом для подобных ситуаций.

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));
}
0 голосов
/ 07 ноября 2019

Хотя это может быть излишним, в случае, подобном вашему, я несколько раз использовал подход псевдо-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;
    }
}

Аккуратно?

0 голосов
/ 07 ноября 2019

Я полностью согласен с подходом @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;
    }
}
0 голосов
/ 07 ноября 2019

Если все классы , которые реализуют ILeasable, имеют понятие области, просто поместите его в ваш ILeasable интерфейс

internal interface ILeasable
{
   int Id { get; set; }
   int AreaInSquareFeet { get; set; }
}

Если этоне тот случай, тогда вам нужно будет пересмотреть вещи. Это будет означать, что ваш текущий код не имеет никакого смысла, и вам придется пересмотреть свой дизайн.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...