Cha*_*rns 5 c# design-patterns .net-4.0
我已经多次遇到过这种情况,所以想用一个真实的例子来了解更有经验的C#开发人员如何处理这个问题.
我正在围绕非托管MediaInfo库编写一个.NET包装器,它收集有关媒体文件(电影,图像......)的各种数据.
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
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,一个非常糟糕的类映射的开始将是一个类,用于特定于每个流类型的功能,以及一个具有所有类型通用功能的基类.
然后这条道路变得不那么明显了.{general,video,audio,text和image}流类型有许多共同的功能.好吧,所以我想我可以创建一个像"GeneralVideoAudioTextImage"这样的臭名字的类,然后另一个名为GeneralVideoAudioText(继承自GeneralVideoAudioTextImage)的类,用于这些事物的共同功能,但不是"图像"流.我想这会尴尬地遵循"等级"的阶级等级规则.
这已经不是很优雅了,但是有一些偶然的情况,比如"Width",它们不适合任何一个干净地成为另一个组子集的组.这些案例可以在必要时简单地复制功能 - 分别在视频,文本和图像中实现,但这显然会违反DRY.
常见的第一种方法是MI,C#不支持.通常的答案似乎是"使用带接口的MI",但我无法完全看到它如何跟随DRY.也许是我失败了.
之前已经讨论过类层次结构,因为有MI的替代方法(扩展方法等),但这些解决方案似乎都不合适.例如,扩展方法似乎更适合用于无法编辑源的类,比如String类,并且更难定位,因为它们并不真正与类绑定,尽管它们可能有效.我没有找到关于这种情况的问题,尽管这可能是我使用搜索工具的失败.
包装的MediaInfo功能的示例可能是:
int _width = int.MinValue;
/// <summary>Width in pixels.</summary>
public int width {
get {
if(_width == int.MinValue)
_width = miGetInt("Width");
return _width;
}
}
// ... (Elsewhere, in another file) ...
/// <summary>Returns a MediaInfo value as an int, 0 if error.</summary>
/// <param name="parameter">The MediaInfo parameter.</param>
public int miGetInt(string parameter) {
int parsedValue;
string miResult = mediaInfo.Get(streamKind, id, parameter);
int.TryParse(miResult, out parsedValue);
return parsedValue;
}
Run Code Online (Sandbox Code Playgroud)
我的问题是:你是如何处理这样的情况,系统是层次结构但不完全?您是否找到了一个相当优雅的策略,或者只是接受了并非每个简单的问题都有一个?
我认为你最好使用接口组合,如果实现比一堆属性更复杂,那么组合提供接口的共享实现:
abstract class Media {
// General properties/functions
}
class VideoAndImageCommon { // Crappy name but you get the idea
// Functions used by both video and images
}
interface IVideoAndImageCommon {
// Common Video & Image interface
}
class Video : Media, IVideoAndImageCommon {
private readonly VideoAndImageCommon _commonImpl = new VideoAndImageCommon();
// Implementation of IVideoAndImageCommon delegates to _commonImpl.
}
class Image : Media, IVideoAndImageCommon {
private readonly VideoAndImageCommon _commonImpl = new VideoAndImageCommon();
// Implementation of IVideoAndImageCommon delegates to _commonImpl.
}
Run Code Online (Sandbox Code Playgroud)