Является ли плохой практикой идентифицировать типы объектов с помощью свойства перечисления вместо использования GetType ()? - PullRequest
2 голосов
/ 17 ноября 2011

У меня есть коллекция объектов, которые реализуют один (пользовательский) интерфейс: IAuditEvent.

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

Метод, который хранит объекты, зацикливается на List<IAuditEvent>, поэтому ему необходимо знать конкретный тип каждого объекта, чтобы сохранить правильный числовой идентификатор.

Не рекомендуется ли иметь свойство перечисления на IAuditEvent, чтобы каждый объект мог идентифицировать свой тип с уникальным значением перечисления?

Я вижу, что самым простым решением было бы написать метод, который переводит Type в целое число, но что если мне понадобится перечисление событий аудита для другой цели? Было бы все еще неправильно иметь мое свойство перечисления на IAuditEvent?

Ответы [ 4 ]

2 голосов
/ 17 ноября 2011

Этот идентификатор типа базы данных (или дискриминатор) по сути является метаданными каждого типа. Смешивание данных и метаданных для каждого экземпляра не очень хорошо Моим предпочтительным решением было бы написать собственный атрибут для хранения этих метаданных, применить его к каждому типу и прочитать их, используя метод GetCustomAttributes из Type.

[DatabaseDiscriminator(123)]
public class MyAuditEvent : IAuditEvent
{
}
1 голос
/ 17 ноября 2011

Краткий ответ: это зависит.

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

Код, который использует интерфейс. Этот код должен знать только о IAuditEvent, а не о его реализующих классах. Если этот код должен знать о различных типах событий аудита (я имею в виду «тип» в самом общем смысле, а не о классах), то я бы сказал, что было бы полезно добавить свойство Type в IAuditEvent. Что касается пользователя, то для каждого типа даже не требуется отдельная реализация.

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

1 голос
/ 17 ноября 2011

Да, это плохо.Теперь вы предполагаете, что каждая реализация IAudit знает о других реализациях, потому что все они должны иметь уникальный идентификатор;Более того, вам нужно добавить новое значение в enum для каждого нового экземпляра интерфейса.Это просто дополнительная информация, которая не нужна внутри приложения, а только в представлении данных.

Скорее всего, есть таблица поиска на бизнес-уровне:

new Dictionary<Type, int> {
    { typeof(UserAudit), 1 },
    { typeof(OrderAudit), 2 }
}
0 голосов
/ 17 ноября 2011

Цель реализации интерфейса состоит в том, чтобы абстрагировать реализацию - IOW вы используете интерфейс и не заботитесь о типе реализации, поэтому не нужно идентифицировать его значением enum.

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

public abstract class BaseType : IAuditEvent
{
    public abstract MyTypeEnum TypeId { get; }

    ... add any base implementation of the interface ...
}

затем в каждом производном объекте:

public class MyConcreteType : BaseType 
{
    public MyTypeEnum TypeId { get { return MyTypeEnum.SpecificValue; } }

    ... any overrides, etc ....
}

У этого подхода есть пара преимуществ:

  • он сохраняет ваш код в чистоте. При реализации интерфейса в ряде классов существует большая вероятность того, что будет некоторая общая реализация интерфейса, которую могут совместно использовать различные объекты, которые могут входить в базовый класс. Разумно используйте методы / свойства abstract и virtual.

  • использование перечисления для идентификации ваших объектов может помочь избежать этих бесконечных и утомительных операторов if (myObj.GetType() == typeof(ObjectA)) {} else if (myObject.GetType() == typeof(ObjectB))..., когда приходит время переходить в зависимости от типа разработчика - теперь вы можете просто использовать оператор switch на основе перечисления возвращается свойством TypeId

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...