Должны ли интерфейсы быть максимально общими? - PullRequest
4 голосов
/ 20 октября 2011

При разработке интерфейсов, если они должны быть настолько универсальными, насколько это возможно, или если вы попытаетесь поместить как можно больше методов, свойств в интерфейс, чтобы сохранить количество интерфейсов на низком уровне: например, лучше 1 или 2:

1) Клиент и Аренда разделены на 2 интерфейса (Данные, относящиеся только к аренде, находятся в интерфейсе Аренда, а данные, относящиеся только к клиенту, находятся в интерфейсе Клиента)

interface ICustomer
{
    public string Name { get; set; }
    public string Address { get; set; }
    public string Phone { get; set; }
    public string Email { get; set; }
 }

interface IRental: ICustomer
{
    string Title { get; set; }
    decimal Cost{ get; set; }
    void Rent();      
}

2) Поместите все данные в один интерфейс.

interface IRental
{
    string Title { get; set; }
    decimal Cost{ get; set; }
    void Rent();  
    public string Name { get; set; }
    public string Address { get; set; }
    public string Phone { get; set; }
    public string Email { get; set; }
}

Также, что касается первого подхода, есть ли преимущество в расширении интерфейса ICustomer или в IRental должно быть свойство ICustomer, например:

interface IRental
{
    ICustomer customer {get;set;}
    string Title { get; set; }
    decimal Cost{ get; set; }
    void Rent();      
}

Каковы преимущества / недостатки вышеуказанных подходов? и есть ли предпочтительный способ (более масштабируемый и обслуживаемый).

Ответы [ 8 ]

8 голосов
/ 20 октября 2011

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

interface Modem
{
     void Dial();
     void Hangup();
     void Send();
     void Receive();
}

. Реализаторы Modem должны предоставлять реализации для набора и отбоя, которые являются проблемами состояния соединения.А затем предоставить реализации для отправки и получения, которые являются проблемами передачи данных.Возможно, это должны быть два уникальных интерфейса, это две разные группы обязанностей, что также входит в Принцип единой ответственности .И не всем модемам может потребоваться оба набора обязанностей.

5 голосов
/ 20 октября 2011

Вы всегда должны принимать во внимание Принцип единой ответственности . Классы, методы и интерфейсы должны быть предметно-ориентированными элементами. Так что ИМХО лучше разделить ICustomer и IRental.

4 голосов
/ 20 октября 2011

Золотого правила не существует.

Лично я разбиваю вещи только тогда, когда мне нужно или когда это имеет смысл.Обычно это означает понимание того, что интерфейс пытается раскрыть, и разрыв логически разных контрактов.

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

В вашем случае IRental и ICustomer - это две логически отдельные сущности.Аренда может содержать информацию о клиенте, но это должно быть сделано в классе клиента, а не через квартиру.Итак, ваш последний код выглядит наиболее разумным для меня:

interface IRental
{
    ICustomer customer {get;set;}
    string Title { get; set; }
    decimal Cost{ get; set; }
    void Rent();      
}
2 голосов
/ 20 октября 2011

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

Прокат не является Заказчиком, поэтому вариант 1 на самом деле не подходит. Задайте себе вопрос «Является ли аренда типом клиента или специализацией клиента»?

Третий вариант, на который вы намекаете, для IRental иметь ссылку на ICustomer, вероятно, имеет смысл, предполагая, что вы не можете арендовать без клиента.

2 голосов
/ 20 октября 2011

Я бы выбрал вариант 1, он позволяет редактировать интерфейсы независимо друг от друга.

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

1 голос
/ 20 октября 2011

Я бы сказал, что этот пример слишком много смешивает. Арендная плата и Клиент - два различных типа лиц.

На первый взгляд, я бы сказал, что у вас есть Тип клиента, Название и Аренда, где Прокат ссылается на Клиента и Название.

    public class Customer
{
    string Name { get; set; }
    string Address { get; set; }
    string Phone { get; set; }
    string Email { get; set; }
}

public class Title
{
    string Name { get; set; }
    decimal Cost { get; set; }
}

public class Rental
{
    Customer Renter { get; }
    Title Media { get; }
    DateTime Due {get;}

    public void Rent(Customer, Title);
}

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

0 голосов
/ 20 октября 2011

Для IRental может быть полезно иметь поле PrimaryCustomer типа ICustomer и, возможно, что-то вроде IList , называемое чем-то вроде RelatedCustomers (в случае, например, если клиент указал, что он планирует использовать элемент с некоторыми другие люди, которые также являются клиентами, такая информация может быть полезна, если есть проблема с арендой, но клиент не может быть достигнут). Однако IRental, вероятно, должен наследовать только ICustomer, если каждый клиент собирается арендовать ровно одну вещь.

В противном случае предположим, что Rental1 и Rental2 оба имеют имя «Джон Смит». Чем будет Rental2.Name после «Rental1.Name =" Фред Джонс "? Было бы неясно, относятся ли Rental1 и Rental2 к одному и тому же клиенту. Если бы была собственность Клиента, такая двусмысленность исчезла бы. Если Object.ReferenceEquals (Rental1.Customer, Rental2.Customer), то изменение Rental1.Customer.Name повлияет на Rental2.Customer.Name (это то же свойство, того же объекта). Если Rental1.Customer является объектом, отличным от Rental2.Customer, то изменение одного не должно влиять на другое.

Между прочим, я хотел бы предложить, что было бы хорошо определить интерфейс IReadableCustomer, в котором свойства просто имеют "геттеры", а ICustomer наследовал от IReadableCustomer (ICustomer должен был бы добавить "новый" спецификатор к определениям своих свойств чтобы избежать глупых «двусмысленных» сообщений). В C # такое изменение не потребовало бы никакого дополнительного кода для реализаций ICustomer, но позволило бы лучше контролировать, кому разрешено вносить изменения в запись клиента.

0 голосов
/ 20 октября 2011

Одна вещь, которая приходит мне в голову - это Принцип разделения интерфейсов из Принципов SOLID. Вместо предоставления одного толстого интерфейса, было бы полезно разделить интерфейсы , если вы думаете, что некоторым клиентам может не понадобиться реализовывать все методы / свойства этого интерфейса. Это также, вероятно, делает ваши намерения просить клиента реализовать интерфейс более понятным, явно сообщая им, что вы хотите, чтобы определенные методы были реализованы для определенных конкретных функций

В отношении второй части вашего вопроса, я думаю, снова может применяться принцип « Композиция над наследованием », хотя это зависит от конкретной проблемы, которую вы пытаетесь решить. Если Customer - это поведение , которое можно переключать во время выполнения, тогда вы должны использовать его как элемент интерфейса IRental, который в основном является предпосылкой Pattern Design Pattern .

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