Пример принципа сегрегации интерфейса C# путаница
Я довольно новичок в программировании, и мне трудно понять, как эффективно применять принцип, показанный в следующей ссылке (банкомат):
http://www.objectmentor.com/resources/articles/isp.pdf
По сути, он начинается с дизайна, в котором нет претензий к ISP (принцип разделения интерфейсов), и продвигается вперед, чтобы реорганизовать поведение в различные интерфейсы.
Мой вопрос: разве мы не используем интерфейсы для выражения общего поведения среди не очень (или нет) связанных абстракций?
Какой смысл инкапсулировать методы в интерфейсе, если даже один из них не будет использоваться совместно с классами, которые будут их реализовывать? В каком сценарии это может быть полезным?
Если мы продолжим строку примера, приведем следующий код:
public interface Transaction
{
void Execute();
}
public interface DepositUI
{
void RequestDepositAmount();
}
public class DepositTransaction : Transaction
{
private DepositUI depositUI;
public DepositTransaction(DepositUI ui)
{
depositUI = ui;
}
public virtual void Execute()
{ /*code*/
depositUI.RequestDepositAmount();
/*code*/ }
}
public interface WithdrawalUI
{
void RequestWithdrawalAmount();
}
public class WithdrawalTransaction : Transaction
{
private WithdrawalUI withdrawalUI;
public WithdrawalTransaction(WithdrawalUI ui)
{
withdrawalUI = ui;
}
public virtual void Execute()
{ /*code*/
withdrawalUI.RequestWithdrawalAmount(); /*code*/
}
}
public interface TransferUI
{
void RequestTransferAmount();
}
public class TransferTransaction : Transaction
{
private TransferUI transferUI;
public TransferTransaction(TransferUI ui)
{
transferUI = ui;
}
public virtual void Execute()
{ /*code*/
transferUI.RequestTransferAmount();
/*code*/
}
public interface UI : DepositUI, WithdrawalUI, TransferUI { }
Насколько я понимаю, чтобы использовать предыдущий дизайн, у нас должно быть что-то вроде:
UI impui = new IMPLEMENTATIONUI(); // Some UI implementation
DepositTransaction dt = new DepositTransaction(Gui);
dt.Execute();
Разве нам не нужно, чтобы IMPLEMENTATIONUI реализовывал каждый отдельный метод? И если так, то не нарушит ли это СРП?
1 ответ
Разве мы не используем интерфейсы для выражения общего поведения среди не очень (или не) связанных абстракций?
Да, в SOLID интерфейсы необходимы для выражения общего поведения. Ваш интерфейс транзакции является превосходным примером этого. От этого зависят классы DepositTransaction и WithdrawlTransaction. ISP (Принцип разделения интерфейса) хочет, чтобы вы разделили это, потому что вам может потребоваться передать объект Transaction в функцию для его выполнения. Все принципы SOLID являются дизайном. Например:
void ExecuteTransaction(Transaction transaction)
{
transaction.Execute();
}
Обратите внимание, что этот метод не зависит ни от чего, кроме интерфейса транзакции. Это инверсия зависимостей.
Если вы не создадите этот интерфейс, вам потребуется создать два разных метода для выполнения операции WithdrawlTransaction или DepositTransaction; вместо этого вы можете использовать метод ExecuteTransaction и передавать все, что реализует Transaction.
ExecuteTransation(withdrawl_TransactionObject);
или же
ExecuteTransaction(deposit_TransactionObject);
или позже в будущем:
ExecuteTransaction(unanticipatedNewTypeOf_TransactionObject);
Разве нам не нужно, чтобы IMPLEMENTATIONUI реализовывал каждый отдельный метод? И если так, то не нарушит ли это СРП?
Интерфейс реализации, вероятно, используется пользователем для взаимодействия с программным обеспечением. У пользователя не будет единой ответственности, теоретически ему / ей придется использовать все интерфейсы, необходимые для класса IMPLEMENTATIONUI.
Я сомневаюсь, что пользовательский интерфейс реализации будет реализовывать все интерфейсы, но он, вероятно, будет использовать все эти интерфейсы для выполнения транзакций. Перефразируя твердые принципы "Дядя Боб", ваш пользовательский интерфейс должен быть полон изменчивого кода, в то время как ваши интерфейсы должны быть наиболее энергонезависимыми.
Является ли наследование одного интерфейса от трех других нарушением интернет-провайдера?
public interface UI : IDepositUi, WithdrawalUI, TransferUI
{
}
Ответ в том, что мы не можем сказать. Это зависит от того, зависит ли клиент от всего интерфейса. Если это так, то это не нарушение интернет-провайдера. Если клиент не зависит от всех трех унаследованных интерфейсов, то это является нарушением, и клиент должен просто зависеть от того интерфейса, который ему нужен.
Как вы заметили, это может нарушить какой-то другой принцип, но это выходит за рамки обсуждения ISP. Но я не думаю, что дело в том, что вы должны создать этот комбинированный интерфейс. Дело в том, что вы можете сохранить небольшие сегрегированные интерфейсы.
У нас может возникнуть соблазн создать гигантский интерфейс и один гигантский класс только потому, что один клиент зависит от всех трех интерфейсов. Но если мы сделаем это, другой клиент, который нуждается только в снятии или передаче, будет вынужден зависеть от более крупного интерфейса, который ему не нужен.
В реальной жизни такого рода вещи выходят из-под контроля, потому что кто-то начинает с широкого, смутно названного интерфейса, такого как ITransactionService
и прежде чем вы это узнаете, разработчики вбрасывают в него кухонную раковину. В примере интерфейсы более конкретно названы. Это не заставит их отделиться, но это поможет. Если дать им такие конкретные имена, это говорит о том, что должно или не должно быть в них. Он предлагает разработчику, который заранее планировал разделять интерфейсы.