OO编程风格 - 虚拟属性,构造函数参数或受保护的setter

时间:2010-11-26 13:34:41

标签: oop

如果我有一个基类(下面的例子中为MyBase)需要子类来提供/覆盖一个值(Value),是否有一种优选的实现方法?我可以想到几种方法,但我没有太多的感觉,为什么我会选择一个而不是另一个。我有一些例子(在C#中,但它们可以是任何OO语言)。

基类中的具体属性 - 子类可以根据需要设置此值。

public class MyBase
{
    protected string Value { get; set; }

    protected MyBase()
    {
        Value = "Default Value";
    }

    private DoSomethingWithValue()
    {
         Console.WriteLine(Value);
    }
}

public class MySub : MyBase
{
    public MySub()
    {
        Value = "Overridden Value";
    }
}

虚拟财产 - 如果愿意,子类可以覆盖它。

public class MyBase
{
    protected virtual string Value { get { return "Default Value"; } }

    protected MyBase()
    {
    }

    private DoSomethingWithValue()
    {
         Console.WriteLine(Value);
    }
}

public class MySub : MyBase
{
    protected override string Value { get { return "Overridden Value"; } }

    public MySub()
    {
    }
}

在基类构造函数中设置的属性 - 子类可以提供值。

public class MyBase
{
    protected string Value { get; private set; }

    protected MyBase(string value)
    {
         Value = value;
    }

    protected MyBase() : this("Default Value")
    {
    }

    private DoSomethingWithValue()
    {
         Console.WriteLine(Value);
    }
}

public class MySub : MyBase
{
    public MySub() : base("Overridden Value")
    {
    }
}

显然,其中一些允许改变价值或动态计算它。但是,在编译时已知该值的情况下,哪种方式更可取,为什么?

2 个答案:

答案 0 :(得分:-1)

如果在编译时为所有可能的派生类知道该值,那么为什么要将它存储在运行时?

更重要的是,当你知道他们都只是return "constant";时,在这里调用虚拟查找是完全浪费的,所以我会选择基地的具体属性。

答案 1 :(得分:-1)

忽略所有OO乱码,并从公共数据成员的简单结构开始。这是最少的工作,最容易理解,最简单易用,最强大。当您更好地理解使用的上下文并确定该类实际上代表一个抽象时,请应用保守的限制并仔细观察代码中断的位置(如果在任何地方)。

除非你有数十年的重数学支持的绝对证明,否则最初使用抽象。在将表示与客户端隔离之前,您必须完全相信您的抽象是完整的。确保在断言或至少注释中声明表示的不变量。如果你不能这样做,你就不能理解创建一个抽象。

例如(在C ++中,抱歉):

class Rational {
  int numerator; int denominator;
public: 
  Rational (int x, int y) { 
    if (y == 0) { throw DivisionByZero; }
    assert (y != 0);
    if (y<0) { x = -x; y = -y; }
    assert (y>0);
    int d = gcd(x,y);
    assert(d>0); // proof, from specs of gcd
    // assert: if there exists an integer q>0 which divides both x/d and y/d
    // then q == 1 (from definition of gcd)

    assert (x % d == 0); // d divides x exactly (from defn of gcd)
    assert (y % d == 0); // d divides y exactly (from defn of gcd)

    numerator = x/d;
    denominator = y/d;

    // assert: provided y != 0, numerator / denominator (math div, not C div)
    // equals input x/ input y (math div, not C div).

    // invariant: denominator > 0, gcd (numerator, denominator) == 1
    // Theorem: representation is unique
  }

  bool eq(Rational other) { 
    numerator == other.numerator && denominator == other.denominator;
    // sufficient by uniqueness theorem
  }
}:

在这种特殊情况下,您应该注意确保表示不变量的计算成本,特别是在实现添加时。也许这不是一个好主意。这完全取决于您的界面是什么样的,以及您将如何使用它。保留不变量在计算上有一个有用的属性,它可以防止乘法上出现可避免的溢出。

在这种情况下,表示是标准规范形式,具有以下类型:

R: int * int - { x,y | y<0 or Exists d. d divides y and d divides x }

通常,表示由笛卡尔积减去子集以生成集合R,以便

R&lt; ==&gt;甲

其中A是抽象类型,是一个双射。

我的观点:获得正确的抽象并不容易。程序员过度使用强大的抽象来创建像类这样的设备,特别是在OO语言中他们不知道,例如,一个简单的结构,这是一个笛卡尔积,已经正式抽象(由函数表示,在这种情况下是投影)。 p>

修复错误的抽象不仅要调整类的接口,还要调整它的每个用法,而不是在使用简单的结构并在重构阶段系统地抽象它时抽象究竟应该是什么。

Yttrill的规则:如果你在考虑抽象..不要!

你需要证据,而不是想到!