我有一个控制电路,它有多种设置,可以连接任意数量的传感器(每个传感器都有自己的设置)。这些传感器只能与控制电路一起使用。我想过使用这样的嵌套类:
public class ControlCircuitLib
{
// Fields.
private Settings controllerSettings;
private List<Sensor> attachedSensors;
// Properties.
public Settings ControllerSettings
{ get { return this.controllerSettings; } }
public List<Sensor> AttachedSensors
{ get { return this.attachedSensors; } }
// Constructors, methods, etc.
...
// Nested classes.
public class Settings
{
// Fields.
private ControlCircuitLib controllerCircuit;
private SerialPort controllerSerialPort;
private int activeOutputs;
... (many, many more settings)
// Properties.
public int ActiveOutputs
{ get { return this.activeOutputs; } }
... (the other Get properties for the settings)
// Methods.
... (method to set the circuit properties though serial port)
}
public class Sensor
{
// Enumerations.
public enum MeasurementTypes { Displacement, Velocity, Acceleration };
// Fields.
private ControlCircuitLib controllerCircuit;
private string sensorName;
private MeasurementTypes measurementType;
private double requiredInputVoltage;
... (many, many more settings)
// Properties.
public string SensorName {...}
... (Get properties)
// Methods.
... (methods to set the sensor settings while attached to the control circuit)
}
}
我已经读过公共嵌套类是“禁忌”,但有例外。这个结构是好还是有更好的选择?
谢谢!
修改
下面是我试图为其编写库类的控制电路的粗略层次结构;我使用代码格式来防止文本换行。
Control Circuit (com. via serial port) -> Attached Sensors (up to 10) -> Sensor Settings (approx. 10 settings per sensor)
Basic Controller Settings (approx. 20 settings)
Output Settings (approx. 30 settings)
Common Settings (approx. 30 settings)
Environment Settings (approx. 10 settings)
所有设置都是通过控制器设置的,但我想要一个有组织的库,而不是只在一个Controller
类下填写所有~100个方法,属性和设置。如果有人可以提供一个简短的例子来概述他们将使用的结构,那将是非常感激的。谢谢!
答案 0 :(得分:22)
类的内容应该是类的实现细节。外部类的嵌套类实现细节,或者您只是将外部类用作方便的名称范围和发现机制?
如果是前者,那么您不应该公开提供私有实施细节。如果它们是该类的实现细节,则将它们设为私有。
如果是后者,那么你应该使用命名空间而不是外部类作为你的范围和发现机制。
无论哪种方式,公共嵌套类都是一个糟糕的代码味道。我希望有一个很好的理由来公开一个嵌套类。
答案 1 :(得分:6)
我没有公共嵌套类的太多问题(一般来说,我不是教条规则的粉丝)但是你考虑过将所有这些类型放在他们自己的命名空间中?这是将类分组在一起的更常见方式。
编辑:只是为了澄清,我很少使用公共嵌套类,我可能不会在这里使用它们,但我也不会完全不喜欢它们。框架中有很多公共嵌套类型的例子(例如List<T>.Enumerator
) - 毫无疑问,在每种情况下,设计师都会考虑使用嵌套类的“气味”,并认为它不是一种气味而是促销该类型是顶级类型,或为所涉及的类型创建新的命名空间。
答案 2 :(得分:4)
从你的评论到Eric的回答:
这些传感器只能用于特定电路
这种关系通常称为依赖。 Sensor
构造函数应该以{{1}}作为参数。嵌套类不传达这种关系。
如果不通过控制器电路,您将无法获取/设置任何传感器设置;
我认为这意味着所有ControlCircuit
属性将在Sensor
使用时委托给(调用)或以某种方式通知(触发事件)。或者,你只有控制电路使用传感器的某种内部接口,使ControlCircuit
不透明类对外界。如果是这种情况,Sensor
只是一个实现细节,可以嵌套为私有或内部(如果您无法对其执行任何操作,也无需“保存”传感器实例)。
另外,我甚至不想暴露传感器构造函数(控制器将有一个方法)
Sensor
构造函数现在采用控制电路的事实足以说明取决于你可以离开构造函数Sensor
的内容。你也可以public
。
我的一般评论是这个设计非常耦合。也许如果你在控制电路,传感器和设置之间有一些接口,那么独立地理解每个组件会更容易,并且设计将更加可测试。我总是觉得有利于制作每个组件显式的角色。也就是说,如果它们不仅仅是实施细节。
答案 3 :(得分:3)
我想说更好的选择是将这些嵌套类移出他们所在的类并让它们独立存在。除非我遗漏了某些东西,否则你只能在主类中使用它们以获得某种范围概念,但实际上,这就是命名空间的用途。
答案 4 :(得分:3)
我一般不同意埃里克。
我通常会考虑的事情是:最终用户应该多久使用一次类型名称ControlCircuitLib.Sensor
。如果它“几乎从不,但类型需要公开,以便做某事可能”,那么去内部类型。对于其他任何事情,请使用单独的类型。
例如,
public class Frobber {
public readonly FrobType Standard = ...;
public readonly FrobType Advanced = ...;
public void Frob(FrobType type) { ... }
public class FrobType { ... }
}
在此示例中,FrobType
仅作为不透明的“事物”。只有Frobber
需要知道它实际上是什么,尽管它需要可以在该类之外传递它。然而,这种例子非常罕见;通常情况下,您应该更愿意避免使用嵌套的公共类。
设计库时最重要的事情之一就是保持简单。因此,使用哪种方式使库和使用代码更简单。
答案 5 :(得分:1)
我喜欢这种情况下的嵌套类,因为它显示了这种关系。如果您不希望外部类的用户能够与外部类分别创建内部类的项,则可以始终隐藏构造函数并使用外部类中的工厂方法来创建内部类的元素。我经常使用这个结构。
答案 6 :(得分:1)
这种结构对我来说似乎是完全合理的。直到今天我还没有意识到微软已经建议不要这样做了,但我还是不知道为什么他们这样做了。
我已经在嵌套类只存在以支持包含类(即它的实现的一部分)的情况下使用了这个结构,但其他类需要能够按顺序查看它与包含类进行交互(即它是类API的一部分)。
话虽这么说,Eric一般都知道他在谈论什么,所以出于对他的知识的尊重,我暂时将这些类转换为使用命名空间。
目前,我不喜欢结果。我有一个名为BasicColumn的类,它只存在于一个名为Grid的类中的列。以前,该类始终被称为Grid.BasicColumn,这很棒。这正是我希望它被引用的方式。现在,Grid和BasicColumn都在Grids命名空间中,它只是简称为BasicColumn,使用了Grids&#39;在文件的顶部。没有任何迹象表明它与Grid的特殊关系,除非我想输入整个命名空间(在Grid I之前有一些前缀,为简单起见而遗漏了)。
如果有人能指出一个实际的原因,为什么使用公共嵌套类在某种程度上适得其反或次优,除了微软并不打算以这种方式使用它们的无关事实,那么我就爱听到它。
答案 7 :(得分:0)
虽然我觉得艾瑞克的答案是正确的,但重要的是要意识到它并没有真正完全解决你的情况。
你的案例听起来非常类似于我经常发现你所拥有的一个类,它实际上是另一个类的实现细节,但是,该子组件的一些细节或功能自然会直接暴露给一些小的不受父母管辖的方面。
在这些情况下,您可以使用接口。嵌套类不必是公共的,因为它们实际上是它们嵌套在其中的类的内部细节,但是功能的一个子集(接口)需要公开可用并且可以由类实现。
这允许构造内部结构由它们嵌套在其中的类控制,同时仍允许从命名空间直接访问类型以进行外部引用。 (调用者将使用SomeNamespace.IChildApi作为名称而不是SomeNamespace.NestingClass.NestedClass.SecondNestedClass.ThirdNestedClass等。)