Я отметил правильный ответ Эндрю Кеннана, потому что я думаю, что это, вероятно, лучший общий подход к несколько сложной иерархии.Тем не менее, я не использовал его предложение в своем окончательном коде.
Я публикую этот новый ответ, чтобы помочь любым будущим пользователям SO, которые ищут этот вопрос из-за схожей проблемы.
Хотя интерфейсы, вероятно, являются лучшим общим решением для чего-то более чисто иерархического, в этом случае они мне не подошли.Я не смог бы выпустить это как проект OSS без использования анонимного имени, если бы я сделал иерархическую группировку.Предполагаемые имена интерфейсов начали пахнуть очень плохо, например, «IGeneralVideoAudioTextImageMenuCommon» и «IVideoAudioTextImageMenuCommon»;декларации о наследовании с 4 или 5 из них были не элегантными, подобных тем, которые человечество ранее не видело:
///<summary>Represents a single video stream.</summary>
public sealed class VideoStream : Media, IGeneralVideoAudioTextImageMenuCommon,
IGeneralVideoAudioTextMenuCommon, IVideoAudioTextImageMenuCommon,
IVideoTextImageCommon, /* ...ad nauseum. */
{
// What would you even name these variables?
GeneralVideoAudioTextImageMenuCommon gvatimCommon;
GeneralVideoAudioTextMenuCommon gvatmCommon;
VideoAudioTextImageMenuCommon vatimCommon;
VideoTextImageCommon vticCommon;
public VideoStream(MediaInfo mi, int id) {
gvatimCommon = new GeneralVideoAudioTextImageMenuCommon(mi, id);
gvatmCommon = new GeneralVideoAudioTextMenuCommon(mi, id);
vatimCommon = new VideoAudioTextImageMenuCommon(mi, id);
vticCommon = new VideoTextImageCommon(mi, id);
// --- and more. There are so far at least 10 groupings. 10!
/* more code */
}
Именование одна из двух трудных проблем в информатике в соответствии сФил Карлтон действительно приближается к более философскому определению «это».Автомобиль "является" транспортным средством, и да, технически автомобиль также "является" MotorcycleCarTruckOrAirplane, но это все о том, чтобы позволить людям понять код, верно?
Я рассматривал гибридный подход, где группировки сНебольшой набор функций будет объединен.Но потом я спросил себя: "Это облегчает понимание кода?"Мой ответ был «Нет», потому что просмотр иерархии наследования подразумевает определенную организацию кода для читателя.Второй метод, вероятно, добавил бы больше сложности, чем было сохранено: не выигрыш.
Мое решение, хотя я все еще считаю, что лучший вариант должен существовать, состоит в том, чтобы поместить все функции, общие для нескольких потоков, в один класс., «MultiStreamCommon», внутренне сгруппированный с использованием #regions:
public class MultiStreamCommon : Media
{
public MultiStreamCommon(MediaInfo mediaInfo, StreamKind kind, int id)
: base(mediaInfo, id) {
this.kind = kind;
}
#region AllStreamsCommon
string _format;
///<summary>The format or container of this file or stream.</summary>
///<example>Windows Media, JPEG, MPEG-4.</example>
public string format { /* implementation */ };
string _title;
///<summary>The title of the movie, track, song, etc..</summary>
public string title { /* implementation */ };
/* more accessors */
#endregion
#region VideoAudioTextCommon
/* Methods appropriate to this region. */
#endregion
// More regions, one for each grouping.
}
Каждый поток создает экземпляр MultiStreamCommon и предоставляет соответствующие функции через средства доступа:
public sealed class VideoStream : Media
{
readonly MultiStreamCommon streamCommon;
///<summary>VideoStream constructor</summary>
///<param name="mediaInfo">A MediaInfo object.</param>
///<param name="id">The ID for this audio stream.</param>
public VideoStream(MediaInfo mediaInfo, int id) : base(mediaInfo, id) {
this.kind = StreamKind.Video;
streamCommon = new MultiStreamCommon(mediaInfo, kind, id);
}
public string format { get { return streamCommon.format; } }
public string title { get { return streamCommon.title; } }
public string uniqueId { get { return streamCommon.uniqueId; } }
/* ...One line for every media function relevant to this stream type */
}
Преимущество состоит в том, что уродство,в то время как все еще там, ограничен классом MultiStreamCommon.Я считаю, что группировки #region гораздо более читабельны, чем каждый потоковый класс, имеющий шесть или около того унаследованных интерфейсов.Как сопровождающий узнает, куда добавить новую функцию MediaInfo?Просто выясните, к каким потокам это относится и поместите его в соответствующий регион.Если они неправильно сгруппированы, это все еще работает и может быть замечено через средство доступа.
Недостатком является то, что медиапотоки не наследуют соответствующую функциональность - средства доступа должны быть записаны.Документация также не наследуется, поэтому каждому потоку понадобится тег для каждого средства доступа, что приведет к дублированию документации.Тем не менее: Дублированная документация лучше, чем дублированный код.
Обеспечение правильной иерархии наследования в этом случае намного сложнее, чем основная проблема, о которой мы должны помнить, это просто обертывание некоторыхlibrary.
Для тех, кто заинтересован в коде, связанном с этим потоком, или в его назначении (простое получение информации о файлах мультимедиа с помощью .NET), кодовая страница Google для этого проекта: здесь .Пожалуйста, дайте мне знать о любых мыслях, особенно от тех, кто опубликовал большое количество прекрасного кода, с которым я столкнулся (Джон Скит, Марк Гравелл и другие).