2012-05-26 14 views
0

我认为,了解语言设计者所期望的默认实践以及他们针对语言改进的默认实践总是有用的,即使稍后会偏离这些实践约定。在Scala中,所有字段都必须在它们声明的类的构造函数中初始化。这是一个重要的限制。次要构造函数也受到限制。构造函数中的任何临时变量都需要放在内部方法或闭包中,以避免不必要的字段,这会使构造函数的主体看起来很乱。所有这些妨碍了使用构造函数体。即使分配给来自超类的抽象变量,也需要覆盖val/var语法,以消除为了构建而使用派生类的一些优点。Scala创建需要复杂构造过程的对象的默认方式

伴侣对象可以访问其类的所有字段。然而,对于构造来说,这并不是它可能首先出现的优点,因为所有的字段都必须在类的构造函数中初始化。所以似乎自然的做法是在对象中使用方法来处理类变量,为类中的每个不可变集合创建一个临时可变集合,默认为listbuffer,然后将所有值和集合传入一个无形的构造函数。工厂可以在任何物体甚至是班级中,但也可以在伴侣物体中,除非有其他充足的理由。对象不能使用类型参数,但如果需要,它们的工厂方法可以使用当然,您可以拥有许多工厂方法,因为您需要准构造函数,并且可以重用任何常用算法。

这是正确的吗?

针对为例请求,这里的构造我从C#对面斯卡拉移植的过程是,注意多类型参数Scala中消失:

public class GridC : GridBase<HexC, SideC, UnitC, ISegC> 
{   
    public Geometry<HexC, SideC, UnitC, ISegC> geomC { get; private set; }   

    internal GridC(Scen scen, int gridNum, int xDim, int yDim, int xOff, int yOff, Terr terr = Terr.Plain): 
     base(gridNum, scen, 10.0) 
    {    
     this.geomC = scen.geomC; 
     xIntLeft = xOff + 1; 
     yIntBottom = yOff; 
     xIntRight = xDim * 2 + 1 + xOff; 
     yIntTop = yDim * 2 + 2 + yOff;    
     Coodg hexCoodg; 
     for (int x = xOff; x < xDim * 2 + xOff; x += 2) 
     { 
      for (int y = yOff; y < yDim * 2 + yOff; y += 2) 
      { 
       if (x % 4 == y % 4) 
       { 
        hexCoodg = new Coodg(num, x + 2, y + 2);       
        HexC hexC = scen.hexCs.NewHexC(hexCoodg); 
        SideC sideC; 
        MiscStrat.sixDirn.ForEach(i => 
         { 
          Coodg sideCoodg = hexCoodg + Cood.DirnTrans(i); 
          sideC = sides[sideCoodg]; 
          if (sideC == null) 
           scen.sideCs.NewSide(hexC, i);         
          else 
           scen.sideCs.SetHex2(sideC, hexC, i); 
         });                   
       } 
      } 
     } 
    } 

以上子类的创建纯粹是为了为以下基类提供一个构造函数,以便显示与构造相关的部分;

public class GridBase<HexT, SideT, UnitT, SegT> : IGridBase 
    where HexT : Hex where SideT : Side where UnitT : Unit where SegT : ISeg 
{ 
    public int num { get; private set; } 
    int IGridBase.num { get { return num; } } 
    IListsGeom<HexT, SideT, UnitT> iLists; 
    public HexList<HexT> hexs { get { return iLists.hexs; } } 
    public SideList<SideT> sides { get { return iLists.sides; } } 
    public Geometry<HexT, SideT, UnitT, SegT> geom { get; private set; }   
    public int xIntLeft { get; protected set; } 
    public int xIntRight { get; protected set; } 
    public int yIntBottom { get; internal set; } 
    public int yIntTop { get; internal set; } 
    public double scale { get; private set; }   

    protected GridBase(int num, IListsGeom<HexT, SideT, UnitT> iLists, double scale) 
    { 
     this.num = num; 
     this.iLists = iLists; 
     this.scale = scale; 
    } 
} 

该构造函数创建一个简单的统一十六进制网格。其他构造函数需要使用完全不同的算法,其他构造函数需要相关但更复杂的算法才能创建。我不是专家,但我的印象是工厂在C#中的使用少得多。

+0

我不确定要理解你的问题。你能否提供一个“复杂的施工过程”的例子? – paradigmatic

+0

@paradigmatic提供的示例 –

+1

构造函数看起来像是附加到伴随对象的主要候选对象,尽管在这种情况下我会试图使它成为一个更具体的命名方法,以清楚它的作用。 –

回答

1

一般来说,您通常会通过您的(基础)类的伴侣对象上的方法来提供像这样的复杂构造。它提供了初始化代码和后期使用代码的完美分离。

此外,拥有不可变数据可能有助于设计更小,更集中的关注点。例如,你有一个框(左,右,上,下),你可以分成它自己的类(或只是一个元组,如果你喜欢)。

2

如果我理解你的问题,我会说,需要一个复杂的构造方法的东西应该应用方法来支持伴侣对象。不过,我担心他们有这么复杂的施工要求。

+0

不创建不可变或基本上不可变的对象往往需要更复杂的构造。在C#中,我相信Java通常具有最小的构造,然后在空闲时填充字段/属性。如果一个人的领域充满了无意义的默认值,强制初始化对我来说似乎没有多大价值。 –

+2

如果你有一堆未分配的字段,这听起来像是一种代码味道,因为该实例不处于其他代码使用的状态。如果您需要输入无意义的默认值,那么听起来代码需要分解或变得更加多态。 –

1

关于您的声明,所有字段必须在类的构造函数中初始化:您有两种方法可以解决这个问题。

  1. 在类的构造函数中将更复杂的字段设置为默认值,例如,val a: Int = _
  2. 不要在同伴对象的适用方法计算并初始化在构造函数中的所有字段(即,通过所有那些预先计算的值来构造)

就个人而言,我更喜欢2,因为它保证构造后对象在语义上完成。

+0

将不可变字段设置为以后无法更改的默认值对于此问题没有多大意义。 – ziggystar

+0

是的,这是事实。我的意见更多地以一般方式表达,但在这种情况下你是对的。 – hiltym

相关问题