我有以下抽象类:
public abstract class FileClient {
public abstract File GetFile(string Path);
public abstract Task<File> GetFileAsync(string Path);
public abstract void MoveFile(string Source, string Destination);
}
我还有多个派生类,它们提供了方法的实现。由于这些方法被声明为public,因此每个类都必须对参数执行参数验证。例如,对于两个GetFile方法,验证方法的代码如下所示:
if (!IsConnected) {
throw new NotConnectedException();
}
if (Path == null) {
throw new ArgumentNullException(nameof(Path));
}
对所有派生类执行上述参数验证的最佳方法是什么?我个人可以想到以下几种方式,随意提及其他方法。
由于代码重复,我个人不喜欢这个解决方案。
在实用程序类或抽象类本身中实现验证方法,并在每个派生类中调用这些验证方法。我不喜欢这个,因为我需要为我想要验证的每个现有方法使用一种新方法。此外,这并没有解决代码重复问题,因为我仍然需要多次调用这些方法。
通过在抽象类的方法中添加验证来实现template method design模式,并定义派生方法将覆盖的新抽象方法。
public File GetFile(string Path) {
...Validation
return DoGetFile(Path);
}
protected abstract File DoGetFile(string Path);
这是我找到的最好的方法,但我不喜欢我有重复的方法名称( Do 后缀除外)和描述。
使方法成为虚拟,并在抽象类中实现验证逻辑。派生类需要先调用基本方法,然后执行自己的逻辑。
public abstract class FileClient {
public virtual File GetFile(string Path) {
...Validation
return null;
}
}
public class FtpClient : FileClient {
public override File GetFile(string Path) {
File file = base.GetFile(Path);
if (file != null) {
return file;
}
...Logic
}
}
这种方法很干净,但我不喜欢它,原因如下:
答案 0 :(得分:3)
使用code contracts的第五种方法。
使用代码契约,您可以使用接口契约实现design by contract,其中整个契约将在编译时自动注入整个接口的所有实现(实际上使用后编译过程)。
您只需要定义IFileClient
接口和合同类。这是最优雅和最强大的解决方案。
请参阅以下代码示例:
[ContractClass(typeof(IFileClientContract))]
public interface IFileClient
{
File GetFile(string path);
Task<File> GetFileAsync(string path);
void MoveFile(string source, string destination);
}
[ContractClassFor(typeof(IFileClient))]
public abstract class IFileClientContract : IFileClient
{
public File GetFile(string Path)
{
Contract.Requires<ArgumentNullException>(!string.IsNullOrEmpty(path));
throw new NotImplementedException();
}
public Task<File> GetFileAsync(string path)
{
Contract.Requires<ArgumentNullException>(!string.IsNullOrEmpty(path));
throw new NotImplementedException();
}
public void MoveFile(string source, string destination)
{
Contract.Requires<ArgumentNullException>(!string.IsNullOrEmpty(source));
Contract.Requires<ArgumentNullException>(!string.IsNullOrEmpty(destination));
throw new NotImplementedException();
}
}
// Any implementation of IFileClient will need to fulfill
// its contracts, including derived classes of FileClient!
public abstract class FileClient : IFileClient
{
public abstract File GetFile(string Path);
public abstract Task<File> GetFileAsync(string Path);
public abstract void MoveFile(string Source, string Destination);
}