Правильное использование интерфейсов и внедрение зависимостей в C#
У меня есть интерфейс "IUser" и класс "Пользователь", реализующий "IUser". Также у меня есть интерфейс для хранилища "IUserRepository".
Я между этими двумя вариантами:
public interface IUserRepository
{
List<User> getAll();
void addUser(User user);
void alterUser(User user);
void deleteUser(string ID);
bool validateLogin(string session_user, string session_pass);
User getUserByID(int ID);
User getUserByNombre(string name);
User getUserBySessionUser(string session_user);
}
и это
public interface IUserRepository
{
List<IUser> getAll();
void addUser(IUser user);
void alterUser(IUser user);
void deleteUser(string ID);
bool validateLogin(string session_user, string session_pass);
IUser getUserByID(int ID);
IUser getUserByNombre(string name);
IUser getUserBySessionUser(string session_user);
}
Вот мое смущение. Должны ли мои методы в интерфейсе репозитория возвращать реальные реализации или интерфейсы? как вы можете видеть в моем первом варианте, я возвращаю "пользовательские" экземпляры. Что я должен вернуть, чтобы свободно соединить код?
Но если я выберу второй вариант в своей реальной реализации пользовательского репозитория, у меня будет доступ только к тем методам и свойствам, которые я объявил в своем интерфейсе IUser.
например, если я вызываю метод addUser из службы, передавая ему экземпляр класса User:
myRepositoryImplementation.addUser(UserInstance);
тогда мой настоящий репозиторий понравится
private readonly IUser user;
public void addUser(IUser _user)
{
this.user=_user;
}
Хороший действительный улов! но тогда у меня будет доступ только к методу и свойствам, объявленным изначально в интерфейсе IUser, а не к любому новому методу или свойству, объявленному в моем экземпляре User.
также, если я вызову метод getUserByID, я получу экземпляр IUser.
Итак, чтобы возобновить это
1). Это правильные подходы?
2). Если это допустимые подходы, какой из них мне следует использовать для сохранения или развязки кода?
3). Если я выберу второй также, если он действителен, тогда я должен объявить в интерфейсе все, что я собираюсь использовать позже? Я имею в виду свойства и методы?
3). Есть идея получше?
3 ответа
1) да, оба действительны
3) да, свойства и методы
2) немного более длинная история. короче говоря, если у вас есть классы POCO, и вы на 100% уверены, что никогда не собираетесь кодировать против каркаса, который не позволяет использовать POCO (например, вместо этого он настаивает на классах, наследуемых от базового класса, специфичного для каркаса), Вы могли бы пойти с первым вариантом (классы).
Но это не всегда работает.
Например, кодирование ваших репозиториев для Linq2SQL и других репозиториев для Entity Framework Code. Во-первых, вы не можете использовать тот же набор классов. Второй (интерфейсный) подход является единственным вариантом. Мы успешно использовали его в нескольких корпоративных приложениях без проблем.
Совет: если вы используете интерфейсный подход, вам обязательно нужен метод для создания пустого экземпляра.
public interface IUserRepository
{
List<IUser> getAll();
...
IUser CreateNew();
}
В противном случае клиент репозитория не имеет других средств для создания экземпляра конкретного типа - тип неизвестен клиенту. Однако некоторые люди склонны переносить метод создания в отдельный класс фабрики.
Я определенно выбрал бы первый вариант. Наличие интерфейса для класса обслуживания, такого как хранилище, полезно, но для класса сущностей, такого как User, не так много.
Как вы сказали, каждый раз, когда вы добавляете свойство, вам также необходимо добавить это свойство в интерфейс, что утомительно. И если предположить, что ваш пользовательский класс является простым классом без зависимостей от других классов, он легко тестируется даже без интерфейса.
Связанное чтение:
http://lostechies.com/jamesgregory/2009/05/09/entity-interface-anti-pattern/
Интерфейсы на объектах - это анти-паттерн - блог Джеймса Грегори
Я думаю, что часть вашей путаницы заключается в том, что у вас есть методы в вашем классе User. Обычно эти классы моделей представляют только данные и не описывают поведение. Таким образом, если вы перенесете свои методы куда-то более подходящее, вся ваша путаница исчезнет, потому что для интерфейса не будет никакой причины.
Другими словами, переместите методы из класса User и сделайте так, чтобы ваш репозиторий работал следующим образом:
List<User> getAll();