重构这个的更好方法?

发布于 2024-09-29 01:01:54 字数 1153 浏览 5 评论 0原文

我有以下类:

public abstract class BaseClass
{
    private readonly double cachedValue;

    public BaseClass()
    {
         cachedValue = ComputeValue();
    }

    protected abstract double ComputeValue()          
}


public class ClassA : BaseClass
{
    protected override double ComputeValue()  { ... }            
}

public class ClassB : BaseClass
{
    protected override double ComputeValue()  { ... }                
}

其中 ComputeValue() 是一项耗时的计算。

问题是,ClassAClassB 中还有其他方法需要从 ComputeValue() 返回的值。我正在考虑在 BaseClass 中添加一个名为“CachedValue”的受保护属性,但我发现这种方法是多余的,并且会让其他程序员感到困惑,他们可能不知道它的存在,并可能直接调用 ComputeValue()。

第二个选项是在派生类级别使用可空类型,因为我不一定要求在 BaseClass 的构造函数中完成计算,惰性计算可能是更好的选择:

protected override double ComputeValue()  
{
    if(cachedValue.HasValue)
    {
        return (double) cachedValue;
    }

    // Do computation
    cachedValue = ...

    return cachedValue;
}        

但我觉得我可以做得更好。

您对此有何看法?

谢谢。

编辑:为了澄清,我试图通过强制使用“cachedValue”来防止 ComputeValue() 被多次调用。

I have the following classes:

public abstract class BaseClass
{
    private readonly double cachedValue;

    public BaseClass()
    {
         cachedValue = ComputeValue();
    }

    protected abstract double ComputeValue()          
}


public class ClassA : BaseClass
{
    protected override double ComputeValue()  { ... }            
}

public class ClassB : BaseClass
{
    protected override double ComputeValue()  { ... }                
}

where ComputeValue() is a time consuming computation.

Problem is, there are other methods in the ClassA and ClassB which require the value returned from ComputeValue(). I was thinking of adding a protected property named 'CachedValue' in BaseClass but I find this approach to be redundant and confusing to other programmers who might not be aware of its existence, and might call ComputeValue() directly.

The second option is to use nullable type at the derived class level as I don't necessarily require the computation to be done in the constructor in BaseClass, lazy computation might be a better option:

protected override double ComputeValue()  
{
    if(cachedValue.HasValue)
    {
        return (double) cachedValue;
    }

    // Do computation
    cachedValue = ...

    return cachedValue;
}        

but I feel I could do better.

What are your thoughts on this ?

Thank you.

Edit: Just to clarify, I'm trying to prevent ComputeValue() from getting called more than once by enforcing the use of 'cachedValue'.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

眉黛浅 2024-10-06 01:01:55

为什么不在构造函数中采用与ComputeValue 具有相同目的的延迟,然后将值公开为受保护的只读字段?

public abstract class BaseClass {
  protected readonly double cachedValue;

  protected BaseClass(Func<double> computeValue) {
    cachedValue = computeValue();
  }
}

Why not take a delagate in the constructor which has the same purpose of ComputeValue and then expose the value as a protected readonly field?

public abstract class BaseClass {
  protected readonly double cachedValue;

  protected BaseClass(Func<double> computeValue) {
    cachedValue = computeValue();
  }
}
从﹋此江山别 2024-10-06 01:01:55

在某些时候你必须初始化这个值,这是没有办法解决的。所以我认为使用可为空值的方法是有意义的 - 我同意它比在那里拥有缓存属性要清晰得多。

您可能想向 ComputeValue 添加一个参数,强制再次计算该值:

protected override double ComputeValue(bool force)   
{ 
    if(!force && cachedValue.HasValue) 
    { 
        return cachedValue; 
    } 

    // Do computation 
    cachedValue = ... 

    return cachedValue; 
}         

At some point you will have to initialize this value, there is no way around that. So I think the method where you use the nullable value makes sense - I agree that it's much clearer than having a cached property in there too.

You might though want to add a parameter to ComputeValue which forces the value to be computed again:

protected override double ComputeValue(bool force)   
{ 
    if(!force && cachedValue.HasValue) 
    { 
        return cachedValue; 
    } 

    // Do computation 
    cachedValue = ... 

    return cachedValue; 
}         
雨的味道风的声音 2024-10-06 01:01:55

如果您使用的是 NET 4,只需使用 Lazy

public class ClassA
{
    Lazy<double> value = new Lazy<double>(()=>something.SomethingComplicated());

    public void AnyMethod()
    {
        double d = value.Value;
        //...
    }

}

请参阅 http://msdn.microsoft.com/en-us/library/dd642331。 aspx

更新:由于您使用的是 .NET 3.5,因此这里有一篇关于自己实现惰性的精彩文章(只有大约 20 个 LOC):http://msdn.microsoft.com/en-us/vcsharp/bb870976.aspx

一个普遍好的建议是始终使用组合而不是继承: )

If you're on NET 4just use Lazy<T>.

public class ClassA
{
    Lazy<double> value = new Lazy<double>(()=>something.SomethingComplicated());

    public void AnyMethod()
    {
        double d = value.Value;
        //...
    }

}

See http://msdn.microsoft.com/en-us/library/dd642331.aspx

Update: Since you're on .NET 3.5, here's a great article about implementing lazy yourself (it's only about 20 LOC): http://msdn.microsoft.com/en-us/vcsharp/bb870976.aspx

A generally good piece of advice is to always use composition over inheritance :)

与之呼应 2024-10-06 01:01:55

另一种方法是在 Baseclass 上添加公共接口方法 GetValue,并且只允许覆盖继承类的 ComputeValue 方法 - 这样您就可以仍然可以扩展功能,但您可以在基类中控制 ComputeValue 结果的行为/上下文,即您可以像示例中那样添加记忆或根据需要进行装饰/扩展。

这遵循 非虚拟接口 (NVI) 模式

    public abstract class BaseClass
    {
        private double? cachedValue;

        public BaseClass()
        {
        }

        protected abstract double ComputeValue();

        public virtual double GetValue()
        {
            if (cachedValue.HasValue)
            {
                return (double)cachedValue;
            }
            else
            {
                cachedValue = ComputeValue();
                return (double)cachedValue;
            }

        }
    }

Another approach would be to add a public interface method GetValue on the Baseclass and only allow to overwrite the ComputeValue method for inherited classes - this way you can still extend the functionality, but you control the behavior/context of the result of ComputeValue in your base class, i.e. you can add memoization like in the example or decorate/extend as needed.

This follows the Non-Virtual interface (NVI) pattern .

    public abstract class BaseClass
    {
        private double? cachedValue;

        public BaseClass()
        {
        }

        protected abstract double ComputeValue();

        public virtual double GetValue()
        {
            if (cachedValue.HasValue)
            {
                return (double)cachedValue;
            }
            else
            {
                cachedValue = ComputeValue();
                return (double)cachedValue;
            }

        }
    }
在梵高的星空下 2024-10-06 01:01:55

需要计算值的逻辑和计算值的逻辑之间是否存在相关的联系?

如果不是,或者至少没有 100% 匹配,您可以将计算逻辑放在单独的类中:CalculatorA 和 CalculatorB,它们都继承自 ICalculator 接口。然后,基类可能是访问该接口并缓存结果的唯一类。

Is there a relevant link between the logic that needs the calculated value, and the logic to calculate the value?

If it isn't, or at least there is no 100% match, you could put the calculation logic in separate classes: CalculatorA and CalculatorB, which both inherit from the interface ICalculator. The Base class could then be the only class that accesses this interface and cache the results.

混浊又暗下来 2024-10-06 01:01:55

我认为 ComputeValue 应该在属性 getter 中延迟调用:

public abstract class BaseClass
{
    private Func<double> _computeValue;

    private double? _cachedValue;
    protected double cachedValue
    {
       get
       {
          if(_cachedValue == null)
             _cachedValue = _computeValue();
          return (double)_cachedValue;
       }
       private set
       {
          _cachedValue = value;
       }
    }

    private BaseClass(){};
    public BaseClass(Func<double> computeValue)
    {
         _computeValue = computeValue;
    }    

}

I think ComputeValue should be lazily called in a property getter:

public abstract class BaseClass
{
    private Func<double> _computeValue;

    private double? _cachedValue;
    protected double cachedValue
    {
       get
       {
          if(_cachedValue == null)
             _cachedValue = _computeValue();
          return (double)_cachedValue;
       }
       private set
       {
          _cachedValue = value;
       }
    }

    private BaseClass(){};
    public BaseClass(Func<double> computeValue)
    {
         _computeValue = computeValue;
    }    

}
浅忆 2024-10-06 01:01:55

为了存储/缓存计算值,您可以使用单例模式,基本上您声明一个静态字段,然后在尝试计算之前检查空值。因此,仅在第一次调用/访问方法或属性时才会计算计算值。如果某些派生类需要不同的计算值,则您可以重写基类方法/属性(需要 virtual 修饰符以确保多态性)。建议对表示类数据的元素使用属性而不是方法。

public abstract class BaseClass {
    private static double _cachedValue = null;
    public BaseClass() { }
    // base class implements a Singleton Pattern to store a computed value
    protected virtual double ComputeValue
    {
        get
        {
            if( _cachedValue == null) { _cachedValue = CalculateComputedValue; }
            return _cachedValue;
        }
    }   
    private double CalculateComputedValue() { return someComplexComputedValue; }
}

public class ClassA : BaseClass
{
    private static double _cachedValue = null;
    //this class does require calculate a specific computed value.
    protected override double ComputeValue
    { 
        get
        {
            if( _cachedValue == null) { _cachedValue = CalculateComputedValue; }
            return _cachedValue;
        }
    }
    private double CalculateComputedValue() { return someComplexComputedValue; }
}

public class ClassB : BaseClass
{
    //this class does not require to calculate compute value, inherited value is used instead.
}

For storing/caching the computed value, you can use the Singleton Pattern, basically you declare a static field then you check for null value before attempt to calculate. So the computed value will be calculated only the first time the method or property is called/acceded. If some derived class require a different computed value the you override the base class method/property (the virtual modifier is required to assure polymorphism). It is recommended to use properties instead of methods for elements that represent a kind of data for the class.

public abstract class BaseClass {
    private static double _cachedValue = null;
    public BaseClass() { }
    // base class implements a Singleton Pattern to store a computed value
    protected virtual double ComputeValue
    {
        get
        {
            if( _cachedValue == null) { _cachedValue = CalculateComputedValue; }
            return _cachedValue;
        }
    }   
    private double CalculateComputedValue() { return someComplexComputedValue; }
}

public class ClassA : BaseClass
{
    private static double _cachedValue = null;
    //this class does require calculate a specific computed value.
    protected override double ComputeValue
    { 
        get
        {
            if( _cachedValue == null) { _cachedValue = CalculateComputedValue; }
            return _cachedValue;
        }
    }
    private double CalculateComputedValue() { return someComplexComputedValue; }
}

public class ClassB : BaseClass
{
    //this class does not require to calculate compute value, inherited value is used instead.
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文