Как избежать дублирования кода при моделировании таблицы, ее макета и записей, которые имеют одинаковую базовую структуру? - PullRequest
5 голосов
/ 13 февраля 2012

Это будет несколько абстрактный вопрос.

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

+-----------+
|    Foo    |
+-----------+
| +Id: Guid |
+-----------+

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

  • Все, что известно в схеме, это имя и тип поля.
  • В конкретной (открытой) таблице "индекс столбца" поля дополнительноизвестны.
  • С записями все эти вещи известны, плюс поле имеет конкретное значение.

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

// these interfaces only need to be implemented once:

interface ISchemaField<T>  {  string Name  { get; }       }


interface ITableField<T>   {  string Name  { get; }        
                              int    Index { get; }       }

interface IRecordField<T>  {  string Name  { get; }        
                              int    Index { get; }       
                              T      Value { get; set; }  }

// these three interfaces are an example for one entity; there would be
// three additional types for each additional entity.

interface IFooSchema
{
    ISchemaField<Guid> Id { get; }
    IFooTable Open(IDbConnection dbConnection, string tableName);
}

interface IFooTable
{
    ITableField<Guid> Id { get; }
    ICollection<IFooRecord> ExecuteSomeQuery();
}

interface IFooRecord
{
    IRecordField<Guid> Id { get; }
}

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

  • Я думал о генерации кода (например, T4), которая была бы приемлемым решением, но я бы предпочел «вручную» кодироватьрешение (если оно есть) с меньшим количеством строк кода и более читаемым кодом.

  • Я думал о создании одного класса для каждой сущности, который реализует интерфейс схемы, таблицы и записи.в то же время ... но это кажется грязным и похоже на нарушение разделения интересов.

Ответы [ 4 ]

1 голос
/ 13 февраля 2012

Создание иерархии наследования интерфейса

interface ISchemaField<T>
{
    string Name { get; }
}

interface ITableField<T> : ISchemaField<T>
{
    int Index { get; }
}

interface IRecordField<T> : ITableField<T>
{
    T Value { get; set; }
}

Классы, реализующие эти интерфейсы, могут затем следовать той же схеме наследования.

1 голос
/ 13 февраля 2012

ИМХО, я думаю, вы зашли слишком далеко с абстракцией структуры таблицы.Чрезмерное использование интерфейсов может затруднить чтение кода.Например, меня очень раздражает, что вы не можете нажать F12, чтобы увидеть реализацию объекта, потому что это тип интерфейса.

Проверенная модель, с которой я работал годами и которую очень легко поддерживать, заключается в том, чтобы все имена классов были идентичны именам таблиц, а затем именам полей, которые соответствуют именам столбцов.Затем вы можете легко генерировать методы, просто используя поиск и замену в редакторе, и то же самое касается изменений кода.Таким образом, вам не нужно хранить имена столбцов и индексы столбцов в памяти.Вы просто жестко закодируете их на своем уровне доступа к данным (HARDCODING НЕ ВСЕГДА ПЛОХО!).Например:

this.Price = reader["Price"] as decimal?;

При таком подходе производительность очень хорошая, а код супер сопровождаемый!

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

1 голос
/ 13 февраля 2012

Посмотрите на AutoMapper . Это небольшая полезная библиотека для преобразования DTO в POCO и т. Д., Которая удалит большой объем дублирующегося кода и позволит перенести данные между слоями вашего приложения.

0 голосов
/ 18 февраля 2012

Я принял другой ответ, но подумал, что поделюсь тем, что я в итоге решил сделать:

  • Я удаляю некоторые дубликаты, объединяя схему таблицы и таблицу в один тип. Это становится типом с состоянием: таблица может быть «не открытой» или «открытой». Если он не открыт, доступны только имена и типы полей. Если таблица была открыта, то также доступны индексы полей.

  • Имена полей - это статические поля или просто строковые константы в типе таблицы.

  • Затем я получаю некоторое дублирование, а именно сходство двух типов: один для отдельной записи, другой для таблицы / схемы, но это приемлемо.

Подводя итог, я выбрал более прагматичное решение, чем мне бы хотелось; это не так чисто, но, вероятно, добьется успеха.


class FooRecord
{
    FooRecord(FooTable table) { … }
    readonly FooTable table;

    public Guid Id { get { … } set { … } }
}

class TableField<T>
{
    public TableField(string name) { … }
    public string Name { get; }
    public int Index { get; internal set; }
}

class FooTable
{
    public void Open(IDbConnection dbConnection, string tableName) { … }
    // ^ opens the table and determines the fields' indices, e.g. Id.Index.

    public TableField<Guid> Id
    {
        get
        {
            return idField;
        }
    }
    TableField<Guid> idField = new TableField<Guid>("ID");
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...