Какой класс C # лучше подходит для чтения и записи, а не для чтения? - PullRequest
13 голосов
/ 10 мая 2010

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


Дизайн класса 1 - предоставить все функциональные возможности в базовом классе, а затем публично представить применимые функциональные возможности в подклассах

public abstract class RepositoryBase
{
    protected virtual void SelectBase() { // implementation... }
    protected virtual void InsertBase() { // implementation... }
    protected virtual void UpdateBase() { // implementation... }
    protected virtual void DeleteBase() { // implementation... }
}

public class ReadOnlyRepository : RepositoryBase
{
    public void Select() { SelectBase(); }
}

public class ReadWriteRepository : RepositoryBase
{
    public void Select() { SelectBase(); }
    public void Insert() { InsertBase(); }
    public void Update() { UpdateBase(); }
    public void Delete() { DeleteBase(); }
}

Дизайн класса 2 - класс чтения-записи наследуется от класса только для чтения

public class ReadOnlyRepository
{
    public void Select() { // implementation... }
}

public class ReadWriteRepository : ReadOnlyRepository
{
    public void Insert() { // implementation... }
    public void Update() { // implementation... }
    public void Delete() { // implementation... }
}

Один из этих дизайнов явно сильнее другого? Если да, то какой и почему?

P.S. Если это звучит как домашнее задание, это не так, но вы можете использовать его как один, если хотите:)

Ответы [ 7 ]

26 голосов
/ 10 мая 2010

Как насчет третьего варианта, тесно связанного с первым, но использующего вместо этого интерфейсы:

public interface IReadRepository {
    public void Select();
}

public interface IWriteRepository {
    public void Insert();
    public void Update();
    public void Delete();
}

// Optional
public interface IRepository : IReadRepository, IWriteRepository {
}

public class Repository : IRepository {
   // Implementation
}

Таким образом, реализация (или может быть) все в одном месте, и различие делается толькопо какому интерфейсу вы смотрите.

5 голосов
/ 10 мая 2010

( Редактировать: Я думаю, что Эрик Петроэль предлагает очень хорошее решение на основе интерфейса в своем ответе. Я бы, вероятно, проголосовал бы за его предложение, прежде всего.)

Из ваших двух вариантов я бы четко проголосовал за design # 2.

С дизайном # 1, Я думаю, что не имеет смысла иметь класс «только для чтения», который внутренне вообще не предназначен только для чтения:

  1. Класс только для чтения "тяжелее", чем должен быть.

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

С дизайном № 2 гораздо понятнее, чем класс «только для чтения», является сокращенной версией (базовым классом) полнофункционального класса или сформулирован по-другому.

1 голос
/ 10 мая 2010

Я бы сказал, дизайн # 2, но тогда вы должны изменить имя ReadOnlyRepository на что-то вроде ReadRepository.

Наследование определяет отношение IS-A между классами, и утверждение, что «ReadWriteRepository - это ReadOnlyRepository», не звучит логично. Но «ReadWriteRepository является ReadRepository» делает.

1 голос
/ 10 мая 2010

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

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

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

0 голосов
/ 20 апреля 2015

Ответ Эрика означает «ПОЛНЫЙ принцип ISP». Это так просто и просто в использовании.

0 голосов
/ 30 января 2011

Я бы предложил иметь базовый класс ReadableFoo с запечатанным производным классом ImmutableFoo, конструктор которого принимает ReadableFoo, и, возможно, наследуемым производным классом MutableFoo, и, возможно, класс ReadableShadowFoo, конструктор которого будет принимать ReadableFoo (который может иметь или не иметь) быть изменяемым), но который будет действовать в качестве оболочки только для чтения.

0 голосов
/ 10 мая 2010

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

...