Как я должен рефакторинг этого дизайна - PullRequest
3 голосов
/ 31 мая 2009

Как мне провести рефакторинг этого дизайна?

class Service : IChargeable, IExtendable<br />
{

}

interface IChargeable
{
decimal? ChargeableAmount { 
    get; 
    }
}

interface IExtendable
{
bool IsExtendable { 
    get; 
    }
}

class DayService : Service { } 
class NightService : Service { } 
class TwentFourService : Service { }

Проблема заключается в том, что эти услуги оплачиваются по разным ставкам, в зависимости от того, являются ли они расширяемыми или в зависимости от их типа.
Процент от взимаемой суммы взимается, если услуга продлена. Плата за обслуживание составляет ChargeableAmount * Сезонная ставка, которая отличается для каждой услуги.

Я не хочу хранить тарифы в классе обслуживания или любом из производных классов. Будет ли хорошей идеей создать поставщика SeasonalRate, у которого есть методы для каждого ServiceType, которые возвращают SeasonalRate?
Проблема в том, что сезонные ставки могут меняться и часто меняются, и мы опасаемся вносить изменения в классы обслуживания. , Мы также хотели бы иметь поддерживаемые зависимости от классов обслуживания в наших классах SeasonalRates.
Является ли хорошей идеей реализовать класс SeasonalRates с перегрузками для каждого ServiceClass (своего рода посетитель)? Будет ли проблемой то, что все классы SeasonalRates должны будут реализовывать методы для всех ServiceTypes?
Какой интерфейс должен реализовывать класс SeasonalRate, поскольку будут также и другие классы, которые рассчитывают скорости, такие как DiscountRate, ZipCodeRate и т. Д .?
Если будет добавлено больше классов ServiceType, будет ли сложно сохранить изменения для всех посетителей?

Редактировать: Все тарифы различаются в зависимости от типа услуги. Например, SeasonalRates для дневных и ночных услуг различаются

Какими могут быть преимущества / недостатки подхода, предложенного Джеффом Фрикаделем Янгом, по сравнению со стандартным шаблоном посетителей следующим образом:

interface IVisitable
{
    Accept(IVisitor visitor);
}
interface IVisitor
{
    Visit(IVisitable visitable);
}
interface IRate
{
    Rate { get; }
}

class Service : IVisitable
{
    public virtual Accept(IVisitor visitor);
}
class Visitor : IVisitor
{
    public virtual Visit(IVisitable visitable) { }
    public virtual Visit(DayService visitable) { }
    public virtual Visit(NightService visitable) { }
}
class RateVisitor : Visitor, IRateVisitor
{
    decimal? _Rate;

    override Visit(IVisitable visitable) { };
    override Visit(DayService visitable) { // Logic Goes here, sets the _Rate Variable };
    // Overrides for other services

    public virtual Rate 
    {
        return this._Rate;
    }
} 

Ответы [ 2 ]

2 голосов
/ 31 мая 2009

Кажется, вы ищете шаблон Стратегии: несколько способов что-то посчитать? просто используйте общий интерфейс и реализуйте различное поведение в конкретных объектах.

А как же:

class Service : IChargeable, IExtendable
{

}

interface IChargeable {  
    decimal? ChargeableAmount { get; }  
}

interface IExtendable {  
    bool IsExtendable { get; }  
}

class RatedService: Service {  
    RateStrategy strategy;  
}

interface RateStrategy {  
    decimal SeasonalRate { get; }  
}

class DiscountRate: RateStrategy {}  
class ZipCodeRate: RateStrategy {}  

class DayService : RatedService { }  
class NightService : RatedService { }  
class TwentFourService : RatedService { }
0 голосов
/ 31 мая 2009

Это интересная проблема. Я хотел бы поразмышлять о цели этого дизайна, чтобы вы могли исправить меня, если я ошибаюсь.

Из вашего описания кажется, что Сервис может иметь несколько Тарифов. Экземпляр службы относится к одному (или нескольким?) Из следующих экземпляров:

SeasonalRate, DiscountRate, ZipCodeRate и т. Д.

Я также понимаю, что существует такая вещь, как объект Visitor, и она относится к одной или нескольким Службам:

DayService, NightService, TwentyFourService и др.

Я считаю, что задача состоит в том, чтобы правильно взимать плату с посетителя, в зависимости от свойств выполняемой Услуги и применяемых тарифов?

Визуализация отношений должна помочь вам понять необходимые интерфейсы. Кажется, что все тарифы должны реализовывать интерфейс, который могут вызывать все сервисы.

public interface IRate { 
  decimal GetRate(Service s);
}

Это позволяет Rates на основе Сервиса решать, каким должно быть возвращаемое значение. Теперь было бы важно, чтобы все Сервисы реализовали интерфейсы (IChargeable, IExtendable и т. Д.), Чтобы все данные, необходимые для принятия решения, были доступны для каждого объекта Rate.

Вот пример того, о чем я думаю:

public interface IThumbnail {
  string ThumbnailFile { get; }
  int ThumbnailSize { get; }
  Image GetThumbnail();
}
public interface IThumbnailProvider { 
  Image GetThumbnail(Service s);
}

public class Thumbnail : IThumbnailProvider { 
    public Image GetThumbnail(Service s) { 
       if(s.ThumbnailFile != null) {
           // get the image
       }
    }
}

public interface ISeasonal {
  SeasonType Season { get; }
}

public class SeasonalRate : IRate {
  public decimal GetRate(Service s) { 
    if(s.Season != SeasonType.None) {
       // return seasonal rate based on s.Season
    } else {
       // throw an exception or do something else
    }
}

public abstract class Service : IChargeable, IExtendable, ISeasonal, IThumbnail { 
  // default implementation for ISeasonal
  protected SeasonType _season = SeasonType.None;
  public SeasonType Season { get { return _season; } }


  // default implementation for IThumbnail
  protected string _thumbFile = null;
  protected int _thumbSize = 32;
  public string ThumbnailFile { get { return _thumbFile; } }
  public int ThumbnailSize { get { return _thumbnailSize; } }
  public Image GetThumbnail() {
     return null; // 
  } 
}

public class DayService : Service { 
  private IRate _confirmedRate;
  private IThumbnailProvider _fileServer;
  public decimal GetRate() { return confirmedRate.GetRate(this); }
  public Image GetThumbnail() { return fileServer.GetThumbnail(); }

  // constructor
  public DayService(IThumbnailProvider fileServer, IRate confirmedRate) { 
        this._season = SeasonType.Day; 
        this._filename = "sunny.jpg"; 
        this._fileServer = fileServer;
        this._confirmedRate = confirmedRate;
  }
} 

Это позволяет вашим объектам Rate изменяться в соответствии с вашими потребностями, но уменьшает изменения, необходимые для каждого Сервиса, поскольку ваши бизнес-переменные должны быть довольно хорошо определены и относительно неизменны в базовом классе Сервиса.

Например, у вашего NightService может не быть миниатюры, поэтому ему не нужно явно реализовывать функцию GetThumbnail. Он просто наследует реализацию по умолчанию в Service.

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