这里是OOP的新手。我用一个方法定义了一个接口,在我的派生类中我定义了另一个公共方法。我的客户端代码有条件地实例化一个接口类型的类,当然编译器不知道其中一个派生类中的方法,因为它不是底层接口定义的一部分。这就是我所说的:
public interface IFileLoader
{
public bool Load();
}
public class FileLoaderA : IFileLoader
{
public bool Load();
//implementation
public void SetStatus(FileLoadStatus status)
{
//implementation
}
}
public class FileLoaderB : IFileLoader
{
public bool Load();
//implementation
//note B does not have a SetStatus method
}
public enum FileLoadStatus
{
Started,
Done,
Error
}
// client code
IFileLoader loader;
if (Config.UseMethodA)
{
loader = new FileLoaderA();
}
else
{
loader = new FileLoaderB();
}
//does not know about this method
loader.SetStatus (FileStatus.Done);
我想我有两个问题:
我应该怎么做才能确定在运行时创建的对象是否具有我尝试使用的方法?或者我的做法是错误的?
我知道人们一直在谈论IOC / DI。作为新的OOP,当我的应用程序问到时,使用IOC的优势是什么? 对于IFileLoader类型,使用具体类x",而不是简单 使用App.Config文件来获取设置?
答案 0 :(得分:1)
参考您的两个问题以及other post我建议您使用以下内容:
您不一定需要在客户端代码中找到运行时的具体实现。按照这种方法,你有点挫败了界面的关键目的。因此,只是天真地使用接口并让后面的具体逻辑决定做什么是相当有用的。
所以在你的情况下,如果一个实现只能加载文件 - 很好。如果你的其他实现能够相同和更多,那也没关系。但客户端代码(在您的情况下是您的控制台应用程序)不应该关心它,只需使用Load()
。
也许有些代码会说超过千言万语:
public class ThirdPartyLoader : IFileLoader
{
public bool Load(string fileName)
{
// simply acts as a wrapper around your 3rd party tool
}
}
public class SmartLoader : IFileLoader
{
private readonly ICanSetStatus _statusSetter;
public SmartLoader(ICanSetStatus statusSetter)
{
_statusSetter = statusSetter;
}
public bool Load(string fileName)
{
_statusSetter.SetStatus(FileStatus.Started);
// do whatever's necessary to load the file ;)
_statusSetter.SetStatus(FileStatus.Done);
}
}
请注意, SmartLoader 会更多。但作为关注点分离的问题,其目的是装载部分。状态设置是另一个类的任务:
public interface ICanSetStatus
{
void SetStatus(FileStatus fileStatus);
// maybe add a second parameter with information about the file, so that an
// implementation of this interface knows everything that's needed
}
public class StatusSetter : ICanSetStatus
{
public void SetStatus(FileStatus fileStatus)
{
// do whatever's necessary...
}
}
最后,您的客户端代码可能类似于以下内容:
static void Main(string[] args)
{
bool useThirdPartyLoader = GetInfoFromConfig();
IFileLoader loader = FileLoaderFactory.Create(useThirdPartyLoader);
var files = GetFilesFromSomewhere();
ProcessFiles(loader, files);
}
public static class FileLoaderFactory
{
public static IFileLoader Create(bool useThirdPartyLoader)
{
if (useThirdPartyLoader)
{
return new ThirdPartyLoader();
}
return new SmartLoader(new StatusSetter());
}
}
请注意,这只是一种可能的方式来执行您正在寻找的内容,而无需在运行时确定 IFileLoader 的具体实现。可能还有其他更优雅的方式,这也引导我进入下一个问题。
首先,将课程责任分开总是一个好主意,特别是如果你想要无痛地对课程进行单元测试。接口是您在这些时刻的朋友,因为您可以轻松地替换或“模拟”实例。利用NSubstitute。此外,小班通常更容易维护。
上述尝试已经依赖于某种控制反转。 main -method几乎不知道如何实例化 Loader (尽管工厂也可以进行配置查找。然后 main wouldn'知道任何事情,它只会使用实例)。
从广义上讲:您可以使用像Ninject或Castle Windsor这样的DI框架,而不是编写样板工厂实例化代码,这样您就可以将绑定逻辑放入最适合的配置文件中你的需求。
简而言之:您可以在app.config中使用布尔 appSetting ,告诉您的代码使用哪种实现。但您可以使用DI-Framework,并利用其功能轻松实例化其他类。对于这种情况,它可能有点过大,但绝对值得一看!
答案 1 :(得分:0)
使用类似:
if((loader as FileLoaderA) != null)
{
((FileLoaderA)loader).SetStatus(FileStatus.Done);
}
else
{
// Do something with it as FileLoaderB type
}
IoC通常用于您的类依赖于需要首先设置的另一个类的情况,IoC容器可以实例化/设置该类的实例供您的类使用,并通常通过构造函数将其注入到您的类中。然后,它会向您提供已设置并准备就绪的课程实例。
编辑:
我只是想让代码简洁易懂。我同意这不是这个代码最有效的形式(它实际上执行了两次演员)。
为了确定特定演员表是否有效,Microsoft建议使用以下表格:
var loaderA = loader as FileLoaderA;
if(loaderA != null)
{
loaderA.SetStatus(FileStatus.Done);
// Do any remaining FileLoaderA stuff
return;
}
var loaderB = loader as FileLoaderB
if(loaderB != null)
{
// Do FileLoaderB stuff
return;
}
我不同意在if中使用is。 is关键字用于确定某个对象是否是从实现特定接口的类实例化的,而不是一个强制转换是否可行。我发现它并不总是返回预期的结果(特别是如果一个类通过直接实现或继承基类来实现多个接口)。