2013-11-12 2 views
1

Моя основная причина использования интерфейса - упростить модульное тестирование. Но я чувствую, что не понимаю, как использовать интерфейсы на 100%.Я правильно использую интерфейсы?

Вот упрощенная версия того, что я пытаюсь сделать:

public interface IPlan 
{ 
    List<Plan> GetPlans(IPlan plan); 
    List<Plan> GetAllPlans(); 
    List<string> DoSomethingElse(); 
} 

public class Plan : IPlan 
{ 
    List<Plan> GetPlans(IPlan plan) 
    { 
     // 
    List<Plan> planList = plan.GetAllPlans(); 

    // 
    // Do stuff with planList... 
    // 
} 

Для создания модульного теста мне нужно, чтобы иметь возможность издеваться класс Plan и возвращает список набора планов из GetAllPlans, поэтому я вижу, что должен использовать интерфейс здесь, чтобы я мог правильно его издеваться. Я также понимаю (или, по крайней мере, я думаю), что я должен передавать объект IPlan в GetPlans, а затем я должен использовать этот объект IPlan для вызова GetAllPlans ... но что-то не похоже на эту настройку.

Я устанавливаю это неправильно? Кажется странным, что я передаю IPlan, который является тем же типом, что и класс, в котором он находится (я бы прошел в IPlan, который является планом). Любые советы по этому поводу?

+1

Извините, я не могу полностью следовать за вами: «Я должен проходить через объект IPlan в GetPlans», передайте как? Вы ничего не проходите. Можете ли вы объяснить, чего вы хотите достичь? – RvdK

+0

@ RvdK: Извините, я забыл включить туда, где я передавал объект IPlan, он был отредактирован. –

ответ

0

Ну, технически вашей реализации является правильным, но для интерфейса кажется странным использовать конкретную реализацию этого интерфейса в своих методах. похоже, IPlan пытается «сделать слишком много».

Я бы вместо того, чтобы иметь что-то вроде этого:

public interface IPlanRepository 
{ 
    IEnumerable<IPlan> GetPlans(IPlan plan); 
    IEnumerable<IPlan> GetAllPlans(); 
    IEnumerable<string> DoSomethingElse(); 
} 

public class PlanRepository : IPlanRepository 
{ 
    IEnumerable<IPlan> GetPlans(IPlan plan) 
    { 
     // fill a List with Plans 
    } 
    IEnumerable<IPlan> planList = GetAllPlans(); 
    { 
     // fill a List with Plans 
    } 
    // 
    // Do stuff with planList... 
    // 
} 

public interface IPlan 
{ 
    // properties and methods relating to a Plan (NOT how to "get" a Plan) 
} 

public class Plan : IPlan 
{ 
    // implementation of properties and methods 
} 

Заметьте, что я также изменить List типы возвращаемых в более общий IEnumerable интерфейс. Это дает вам гибкость в реализации, чтобы вернуть что-нибудь, которое возвращает IEnumerable<Plan>, например List<Plan>, Plan[] или IQueryable<Plan>, если вы используете ORM.

0

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

, что я не понимаю, в вашем вопросе ...

«Это кажется странным, что я пропускание IPlan, что к тому же типу, что и класс, что он находится в (я проездом в IPLAN, который является планом). «

вы можете уточнить?

, если вы имеете в виду список GetPlans(), тогда подобные вещи полностью прекрасны и ожидаются. вы планируете вернуть коллекцию планов.

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

Возможно, вам нужен класс сущностей, называемый Plan, а затем переименуйте текущий класс Plan, который реализует IPlan для PlanOperations или что-то еще. Кажется, что этот класс будет выполнять свою работу, а ваш план - это объект или объект, который может получить сеттеры.

0

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

Предназначен для ослабления сцепления. Вместо того, чтобы ваш метод принимающего объект плана, например:

public bool DoSomething(Plan plan) 
{ 
//.... 
} 

Вы можете сделать это взять IPlan:

public bool DoSomething(IPlan plan) 
{ 
//.... 
} 

Это означает, что во время выполнения, вы могли бы передать что-нибудь в этот метод, до тех пор, как это реализует IPlan.

Это также означает, что для модульного тестирования вы можете создать фиктивную реализацию IPlan (например, если IPlan обычно связывается с БД, вы не хотите, чтобы это происходило в вашем тесте) и передавали его - так, чтобы вы проверяете только код в методе, а не тот метод и все, что он вызывает внутри плана.

+0

Спасибо, ваш второй пример (DoSomething (план IPlan)) более конкретно то, о чем мне интересно - правильно ли передать объект IPlan в классе, реализующем этот интерфейс? –

+0

В качестве расширения для «ослабленной связи» интерфейсы позволяют любому классу участвовать в _system другого кода, независимо от базового типа/пространства имен/сборки. Вы получаете гибкость для добавления реализаций в класс без повторного проектирования всего. Это позволяет вам «подделывать» множественное наследование ... – Gusdor

+0

@kevinrcress Вы можете сделать. Это зависит от того, что вы намерены делать, я полагаю. Ваш класс уже знает о себе, поэтому, если он возвращает список сам по себе, вам вообще не нужно будет передавать объект IPlan. Чаще всего передавать IPlan методам * not *, содержащимся внутри класса, реализующего его. –

2

Ваши путаницы, я думаю, от природы вашего класса Plan. У вас есть класс, который предположительно инкапсулирует некоторые данные плана, но также предоставляет методы для обработки списков.

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

List<IPlan> GetPlans(); 
List<IPlan> GetAllPlans(); 

из, возможно, даже для класса быть универсальным, ограничена по T : IPlan А.Н., таким образом, подписи являются:

List<T> GetPlans(); 
List<T> GetAllPlans(); 
+0

Спасибо, я думаю, что вижу, что вы говорите. Если я оставлю класс Plan в качестве определения Плана и добавлю класс, который _does_ stuff с планами, я бы переместил GetPlans и GetAllPlans там, и, вероятно, все станет больше смысла. Или вы предлагаете что-то еще? –

+0

@kevinrcress Это действительно то, что я предлагаю. –

Смежные вопросы