Проектирование классов для системы, которая является иерархической, но не совсем четкой

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

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

MediaInfo имеет множество функций, каждая из которых применяется к разным типам файлов. Например, «PixelAspectRatio» применяется к изображениям и видео, но не к аудио, субтитрам и т. д.

Подмножество функций, которые я хотел бы обернуть, приведено ниже:

General Video   Audio    Text   Image  Chapters  Menu    (Name of function)    
x       x       x        x      x      x         x       Format
x       x       x        x      x      x         x       Title
x       x       x        x      x      x         x       UniqueID
x       x       x        x      x                x       CodecID
x       x       x        x      x                x       CodecID/Hint
        x       x        x      x                x       Language
x       x       x        x      x                        Encoded_Date
x       x       x        x      x                        Encoded_Library
x       x       x        x      x                        InternetMediaType
x       x       x        x      x                        StreamSize
        x       x        x      x                        BitDepth
        x       x        x      x                        Compression_Mode
        x       x        x      x                        Compression_Ratio
x       x       x        x                       x       Delay
x       x       x        x                       x       Duration
        x       x        x                               BitRate
        x       x        x                               BitRate_Mode
        x       x        x                               ChannelLayout
        x       x        x                               FrameCount
        x       x        x                               FrameRate
        x       x        x                               MuxingMode
        x       x        x                               MuxingMode
        x       x        x                               Source_Duration
        x                x      x                        Height
        x                x      x                        Width
        x                       x                        PixelAspectRatio
                x                                        SamplingRate
x                                                        Album
x                                                        AudioCount
x                                                        ChaptersCount
x                                                        EncodedBy
x                                                        Grouping
x                                                        ImageCount
x                                                        OverallBitRate
x                                                        OverallBitRate_Maximum
x                                                        OverallBitRate_Minimum
x                                                        OverallBitRate_Nominal
x                                                        TextCount
x                                                        VideoCount

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

Затем этот путь становится немного менее очевидным. Существует много функций, общих для типов потоков {общий, видео, аудио, текст и изображение}. Итак, я думаю, я могу создать класс с вонючим именем, например «GeneralVideoAudioTextImage», а затем еще один с именем GeneralVideoAudioText (который наследуется от GeneralVideoAudioTextImage) для функций, общих для этих вещей, но не для потоков «Image». Я думаю, это будет неуклюже следовать правилу «является» иерархии классов.

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

Распространенным первым подходом будет MI, который C# не поддерживает. Обычный ответ на это, кажется, «использовать MI с интерфейсами», но я не могу полностью понять, как это может следовать за DRY. Возможно, мой провал.

Иерархии классов уже обсуждались на SO ранее, как и альтернативыMI (методы расширения и т. д.), но ни одно из этих решений не показалось подходящим. Например, кажется, что методы расширения лучше использовать для классов, исходный код которых вы не можете редактировать, таких как класс String, и их сложнее найти, потому что они на самом деле не связаны с классом, хотя они могут работать. Я не нашел вопроса о подобной ситуации, хотя, вероятно, это связано с тем, что я не использовал инструмент поиска.

Примером обернутой функции MediaInfo может быть:

int _width = int.MinValue;
/// Width in pixels.
public int width {
    get {
        if(_width == int.MinValue)
            _width = miGetInt("Width");
        return _width;
    }
}

// ... (Elsewhere, in another file) ...
/// Returns a MediaInfo value as an int, 0 if error.
/// The MediaInfo parameter.
public int miGetInt(string parameter) {
    int parsedValue;
    string miResult = mediaInfo.Get(streamKind, id, parameter);
    int.TryParse(miResult, out parsedValue);
    return parsedValue;
}

У меня такой вопрос: как вы справлялись с подобными ситуациями, когда системы являются своего рода иерархическими, но не совсем? Вы нашли достаточно элегантную стратегию или просто смирились с тем, что она есть не у каждой простой задачи?

5
задан Community 23 May 2017 в 11:45
поделиться