gpt4 book ai didi

c# - 我怎样才能改进这个设计?

转载 作者:可可西里 更新时间:2023-11-01 03:09:40 25 4
gpt4 key购买 nike

让我们假设我们的系统可以执行操作,并且一个操作需要一些参数来完成它的工作。我为所有操作定义了以下基类(为了您的阅读乐趣而进行了简化):

public abstract class BaseBusinessAction<TActionParameters> 
: where TActionParameters : IActionParameters
{
protected BaseBusinessAction(TActionParameters actionParameters)
{
if (actionParameters == null)
throw new ArgumentNullException("actionParameters");

this.Parameters = actionParameters;

if (!ParametersAreValid())
throw new ArgumentException("Valid parameters must be supplied", "actionParameters");
}

protected TActionParameters Parameters { get; private set; }

protected abstract bool ParametersAreValid();

public void CommonMethod() { ... }
}

只是BaseBusinessAction的具体实现知道如何验证传递给它的参数是否有效,因此 ParametersAreValid是一个抽象函数。但是,我希望基类构造函数强制传递的参数始终有效,所以我添加了一个调用 ParametersAreValid到构造函数,当函数返回 false 时我抛出异常.到目前为止一切顺利,对吧?好吧,不。代码分析告诉我“not call overridable methods in constructors”这实际上很有意义,因为当调用基类的构造函数时子类的构造函数尚未被调用,因此 ParametersAreValid方法可能无法访问某些关键成员变量子类的构造函数将被设置。

所以问题是:我该如何改进这个设计?

我要添加 Func<bool, TActionParameters> 吗?基类构造函数的参数?如果我这样做了:

public class MyAction<MyParameters>
{
public MyAction(MyParameters actionParameters, bool something) : base(actionParameters, ValidateIt)
{
this.something = something;
}

private bool something;

public static bool ValidateIt()
{
return something;
}

}

这会起作用,因为 ValidateIt是静态的,但我不知道...有没有更好的方法?

非常欢迎评论。

最佳答案

这是继承层次结构中常见的设计挑战——如何在构造函数中执行类相关行为。代码分析工具将此标记为问题的原因是此时派生类的构造函数尚未有机会运行,并且对虚方法的调用可能依赖于尚未初始化的状态。

所以你在这里有几个选择:

  1. 忽略这个问题。如果您认为实现者应该能够在不依赖类的任何运行时状态的情况下编写参数验证方法,那么请记录该假设并坚持您的设计。
  2. 将验证逻辑移到每个派生类构造函数中让基类只执行它必须执行的最基本、抽象类型的验证(空检查等)。
  3. 在每个派生类中复制逻辑。这种代码重复似乎令人不安,它为派生类打开了大门,忘记执行必要的设置或验证逻辑。
  4. 提供某种类型的 Initialize() 方法,该方法必须由消费者(或您的类型的工厂)调用,以确保在完全构建类型后执行此验证。这可能是不可取的,因为它要求实例化您的类的任何人都必须记住调用初始化方法——您认为构造函数可以执行该方法。通常,工厂可以帮助避免这个问题 - 它是唯一允许实例化您的类的工厂,并且会在将类型返回给消费者之前调用初始化逻辑。
  5. 如果验证不依赖于状态,则将验证器分解为一个单独的类型,您甚至可以将其作为通用类签名的一部分。然后您可以在构造函数中实例化验证器,将参数传递给它。每个派生类都可以定义一个带有默认构造函数的嵌套类,并将所有参数验证逻辑放在那里。下面提供了此模式的代码示例。

如果可能,让每个构造函数执行验证。但这并不总是可取的。在那种情况下,我个人更喜欢工厂模式,因为它使实现简单明了,而且它还提供了一个拦截点,以后可以在其中添加其他行为(日志记录、缓存等)。然而,有时工厂没有意义,在这种情况下,我会认真考虑创建独立验证器类型的第四种选择。

这是代码示例:

public interface IParamValidator<TParams> 
where TParams : IActionParameters
{
bool ValidateParameters( TParams parameters );
}

public abstract class BaseBusinessAction<TActionParameters,TParamValidator>
where TActionParameters : IActionParameters
where TParamValidator : IParamValidator<TActionParameters>, new()
{
protected BaseBusinessAction(TActionParameters actionParameters)
{
if (actionParameters == null)
throw new ArgumentNullException("actionParameters");

// delegate detailed validation to the supplied IParamValidator
var paramValidator = new TParamValidator();
// you may want to implement the throw inside the Validator
// so additional detail can be added...
if( !paramValidator.ValidateParameters( actionParameters ) )
throw new ArgumentException("Valid parameters must be supplied", "actionParameters");

this.Parameters = actionParameters;
}
}

public class MyAction : BaseBusinessAction<MyActionParams,MyActionValidator>
{
// nested validator class
private class MyActionValidator : IParamValidator<MyActionParams>
{
public MyActionValidator() {} // default constructor
// implement appropriate validation logic
public bool ValidateParameters( MyActionParams params ) { return true; /*...*/ }
}
}

关于c# - 我怎样才能改进这个设计?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2448920/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com