2

因此,对于继承,我总是发现自己在处理最具体的类中极长的构造函数。我正在寻找一种可以避免这个问题的范例或方法。我曾想过使用工厂,但如果我这样做了,我仍然必须使用工厂中的构造函数。不管这种想法,我能做些什么来降低构造函数参数的长度吗?

例如:

abstract class DrawableComponent : Component, ITransformable, IScalable, IDrawable, IRotatable

是一个带有构造函数的子类

DrawableComponent(string Name, int SizeX, int SizeY, int X, int Y, float Rotation) : base(Name) { ... }

现在让我们说我想做一个孩子。事情变得更加复杂和丑陋。

class TextBoxComponent : DrawableComponent, IRenderable

构造函数现在变得非常长:

public TextBoxComponent(
    IDrawableUnit Background,
    IDrawableUnit Text,
    string Name,
    int X,
    int Y,
    int SX,
    int SY)
: base(Name, SX,SY,X,Y,0.0f)
{
    this.Background = Background;
    this.Foreground = Foreground;
}

总之,我厌倦了这样的事情:

class Blah {Blah(blah)}
class childBlah : Blah {childBlah(blah,blahs,blaha) : base (blah)}
class grandChildBlah : childBlah {grandChildBlah(blah, blahs, blaha, blaht, blauh) : base(blah,blahs,blaha) }
4

4 回答 4

5

如果你有很长的构造函数参数列表,那么你必须在一个类中有很多字段?这表明您应该考虑将这些类拆分为多个类,或者将相关字段组提取到它们自己的类型中。

例如,x 和 y 坐标字段或宽度和高度值:

private double mX;
private double mY;
private double mWidth;
private double mHeight;

实际上应该是一个点或向量或矩形对象:

private vector mPosition;
private vector mSize;

// or

private rectangle mRegion;

另一个建议可能是减少您的继承,而是使用组合。代替is-a关系,使用has-a关系:

然后,您的“废话”示例将变为:

class Blah
{
   Blah(int blah) { ... }
}

class ChildBlah
{
   Blah mBlah;
   ...
   ChildBlah(Blah blah, int blahs, int blaha)
   { mBlah = blah; ... }
}

class GrandChildBlah
{
   ChildBlah mChildBlah;
   ...
   GrandChildBlah(ChildBlah blah, int blaht, int blahu)
   { mChildBlah = blah; ... }
}
于 2012-05-23T22:06:58.627 回答
2

是的,您可以使用 Builder 模式。至少,这会将对构造函数的调用限制为一个类。例如:

VehicleBuilder builder = new VehicleBuilder();
builder.setColor( Color.Green );
builder.setEngineSize( EngineSize.Size1500 );
builder.setWheelSize( 14 );
Vehicle auto = builder.makeVehicle();

另一种可能性是将其中一些参数划分为类。即使您正在创建一个不可变对象,如果您制作防御性副本,它也可以提供可变参数。

VehicleParameters parameters = new VehicleParameters();
parameters.setColor( Color.Green );
parameters.setEngineSize( EngineSize.Size1500 );
parameters.setWheelSize( 14 );
Vehicle auto = new Vehicle( parameters );
于 2012-05-23T22:06:38.220 回答
0

你可以做几件事。构造函数链接可选参数

构造函数链接本质上意味着您有一个默认构造函数,然后为每个附加参数或根据需要多一个构造函数。

像这样:

class Class1
{
    private string name, jobTitle, address;

    public Class1(string name, string title) 
        : this(name, title, "my default address") {}

    public Class1(string name, string jobTitle, string address)
    {
        this.name = name;
        this.jobTitle = jobTitle;
        this.address = address;
    }
}

另一个选项是可选参数,如下所示:

class Class1
{
    private string name, jobTitle, address;

    public Class1(string name = "default", string jobTitle = "defaultJob", string address = "defaultAddress")
    {
        this.name = name;
        this.jobTitle = jobTitle;
        this.address = address;
    }
}

请记住,必需参数必须始终位于可选参数之前。

于 2012-05-23T22:23:01.527 回答
0

您可以通过传入包含您感兴趣的属性的键值对字典来减轻痛苦。为了更好的可读性和语法,该字典可以从匿名类构建,就像 ASP.NET MVC 之类的库一样它用于很多方法调用。

然后,您将在每个级别有两个构造函数:一个接受字典的受保护构造函数和一个接受匿名对象的公共构造函数,它本身总是会调用受保护构造函数,并将匿名对象转换为字典。

public class Class1 {
  public Class1(object args): this(new RouteValueDictionary(args)) {}

  protected Class1(RouteValueDictionary args) {
    // retrieve any property you need here from args
  }
}

public class Class2: Class1 {
  public Class2(object args): this(new RouteValueDictionary(args)) {}

  protected Class2(RouteValueDictionary args): base(args) {
    // retrieve any property specific to Class2 from args
  }
}

// etc.

像这样使用:

new Class2(new {
    X: 10,
    Y: 20,
    Name: "instance"
  });

优点是无论传入多少参数,这都会为您提供一个很好的语法,并且您还可以省略参数或创建构造函数的非常动态的“重载”。这种方法的主要问题是编译器无法在编译时告诉您需要将什么传递给构造函数(例如,您失去了一些静态分析的好处)。

Note: the RouteValueDictionary used here for parsing is not part of the MVC project, but of the System.Web assembly, so that it is readily available everywhere where the full .NET framework has been deployed. It does handle the anonymous type extraction efficiently, usually better than reflecting it manually yourself.

于 2012-05-23T23:07:53.113 回答