Это интересная проблема. Я хотел бы поразмышлять о цели этого дизайна, чтобы вы могли исправить меня, если я ошибаюсь.
Из вашего описания кажется, что Сервис может иметь несколько Тарифов. Экземпляр службы относится к одному (или нескольким?) Из следующих экземпляров:
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.