class Message{}
class MessageA : Message { }
class MessageB : Message { }
class MessageC : Message { }
Существуют несколько способов решения данной задачи, и первое что приходит в голову написать следующее:
void OnReceive(Message msg)
{
if(msg is MessageB)
{
// Делаем запись в лог, что
// получили сообщение типа B
}
}
очевидно, что такое быстрое решение не очень красивое, так если мы заходим логировать приход сообщения типа MessageC, то придется расширять if-условие.
Тогда на ум приходит идея ввести некий маркер, и если вспомнить про Marker Interface pattern, то мы получим следующую реализацию:
interface ILogMarker
{
// Пусто, интерфейс-маркер.
}
class Message{}
class MessageA : Message { }
// пометим маркером ILogMarker
class MessageB : Message, ILogMarker
{
}
class MessageC : Message { }
void OnReceive(Message msg)
{
if(msg is ILogMarker)
{
// Делаем запись в лог, что
// получили сообщение помеченное
// как ILogMarker.
}
}
Уже намного лучше, условие if у нас уже фиксировано. Единственное что должно смутить, это странное сочетание типа MessageB и интерфейса ILogMarker, ведь ILogMarker несет в себе метаинформацию, а не саму информацию о типе.
Но на этом можно было бы и остановиться, если бы не наличие такого инструмента как Custom attributes, и в этом случае, использовать "Marker Interface" при проектировании типов не рекомендуется (Interface Design: Avoid using marker interfaces (interfaces with no members)).
Итак, принимая во внимание данную рекомендацию:
// Custom attribute-маркер
class LogAttrubute : Attribute
{
}
class Message{}
class MessageA : Message { }
// пометим маркером LogAttrubute
[LogAttrubute]
class MessageB : Message, ILogMarker
{
}
class MessageC : Message { }
void OnReceive(Message msg)
{
if (msg.GetType().IsDefined(typeof(LogAttrubute), false))
{
// Делаем запись в лог, что
// получили сообщение помеченное
// как LogAttrubute.
}
}
Итого, мы получили фиксированное if-условие, метаданные о типе сообщения вынесены в Custom attributes, а не в сам тип в случае Marker Interface.
Ссылки:
А просто field IsRequiredLogging в message добавить не судьба?
ОтветитьУдалить@mphome
ОтветитьУдалитьIsRequiredLogging - не относится конкретно к типу Message, это ведь как бы служебная информация, зачем ее примешивать?
Звучит логично с точки зрения проектирования. :)
ОтветитьУдалитьТолько вот как быть если надо к примеру логированием "динамично" управлять? В ран-тайме разве можно добавить custom attribute? (про интерфейс я вообще молчу). Как быть?
Думаю надо расширить атрибут каким-нибудь свойством-условием:
ОтветитьУдалитьclass LogAttrubute : Attribute
{
/* Добавить свойство-условие, при выполнении которого осуществлять логирование */
}
а в OnReceive, получать атрибут у msg и проверять.
Илья, как-то монстроидально это выглядит. Как по мне static field в таком случае практичней (что не означает - "правильнее" :) ).
ОтветитьУдалитьХотя Вы правы что к самому message это никакого отношения не имеет. И его там быть не должно.
О том, что оба подхода нормальные (не монстроидальные) говорит тот факт, что они используются в самом .NET Framework.
ОтветитьУдалитьНапример, интерфейс INamingContainer и атрибут Serializable.
@veselovski
ОтветитьУдалитьВозможно ответ предпочтения подхода кроется в сравнении быстродействии/сложности двух конструкций:
"msg is ILogMarker"
против
"msg.GetType().IsDefined(typeof(LogAttrubute), false)"
А по мне - проблема надумана. Оба подхода стоят друг друга и тут в большей степени выбор сводится к вкусам конкретного разработчика/архитектора.
ОтветитьУдалитьХотя я обычно предпочитаю использовать атрибуты.
Согласен с Sergun - реально никакой проблемы нет, т.к. оба подхода валидны, хотя конечно `is` всяко лучше чем reflection. Лично я не использую ни то ни другое - я фанат конфигурируемых контейнеров, в которых потом можно "передумать" и поменять подход без особых телодвижений.
ОтветитьУдалитьЯ бы вообще не хранил в сообщении информацию о том, требуется его логировать или нет.
ОтветитьУдалитьПо-моему лучше когда список типов логируемых сообщений хранится в конфигурационном файле и при старте загружается оттуда.