许多构造函数参数 - 有更好的方法吗?
Lots of constructor parameters - Is there a better way?
public class HourlyForecastData
{
public DateTime DateTime { get; private set; }
public decimal TemperatureCelcius { get; private set; }
public decimal DewPoint { get; private set; }
public string Condition { get; private set; }
public int ConditionCode { get; private set; }
public int WindSpeed { get; private set; }
public string WindDirection { get; private set; }
public decimal WindDegrees { get; private set; }
public int UltravioletIndex { get; private set; }
public decimal Humidity { get; private set; }
public decimal WindChill { get; private set; }
public int HeatIndex { get; private set; }
public decimal FeelsLike { get; private set; }
public decimal Snow { get; private set; }
public HourlyForecastData(DateTime dateTime, decimal temperatureCelcius, ...)
{
DateTime = dateTime;
TemperatureCelcius = temperatureCelcius;
//...set all the other properties via constructor
}
}
我正在努力学习更好的软件设计和 OOP。我正在创建一个库,该库可以访问以 XML 回复的天气服务。该服务提供了许多不同的字段,因此我为每个 XML 字段创建了属性。但是,通过构造函数设置那么多属性感觉有点乱。我可以省略构造函数并使用 public 设置器,但我正在尝试制作一个不可变的 class.
我为此查看了不同的设计模式,似乎有一些 "Builder" 和 "Factory" 模式。但是,我很难理解如何将其应用到我的代码中。或者我应该使用完全不同的东西来填充这些对象中的属性?
Re 是否有更好的 OOP 方法?
class 上的大量属性通常表明需要拆分 class(SOLID 的 Single Responsibility Principle)。
例如看起来 HourlyForecastData
对风(速度和方向)、降水(雪、露和雨)和温度(最小值、最大值...)建模,这些问题可以拆分为单独的 classes,然后 HourlyForecastData
将是三者的组合。
回复:建造者模式
构建器模式可用于减轻构建大型(通常是不可变的)classes 或图形期间的负担,但显然需要额外的(可变的)Builder class(es) 来构建目标 class 表示(即 HourlyForecastData
)并最终创建它(即,通过将所有参数传递给构造函数来不可变地构造它)。因此,如果那是 'better' 所要求的,那么这并不会减少工作量,但这当然可以更容易阅读,例如:
HourlyForecastData todaysForecast = new HourlyForecastDataBuilder()
.WithBaseline(ObjectMother.WinterSnow) // Provide an archetype
.WithPrecipitation(snow: 5, rain:1) // Dew defaults to 0
.Build();
基线原型/object mothers 如果某个地区的天气模式经常稳定并且只需要进行小幅调整,则将很有用。 IMO 构建器模式在测试中最有用。我看不出明显适合 Xml 序列化用法。
另见 Named and Optional parameters
回复:不变性
私有 setter 在技术上仍然允许可变性,尽管在 class 本身内受到限制。 C#6 及更高版本支持 getter-only auto properties,这是实现不可变属性的最简单形式
public class HourlyForecastData
{
public DateTime DateTime { get; }
...
public HourlyForecastData(DateTime dateTime, ...)
{
// Get only auto properties can only be set at construction time
DateTime = dateTime;
...
不相关,但 Scala offers an even more concise syntax than C# 用于在 class 上定义不可变的 public 属性,方法是在(主)构造函数中声明一次:
class HourlyForecastData(val temperature: Int, val station: String, ...) {
}
不需要任何进一步的 属性 或支持字段,同时表达和执行不变性。但是,调用者仍然需要承担提供所有参数的负担(无论是直接提供,还是通过 Builder 等提供)。
回复:Xml
如果您提供 API,我建议您使用 WebAPI。我建议不要将 Xml 序列化问题构建到您的 DTO classes 中,而是依赖 Content Negotiation。这将允许调用者确定是否应以 Xml 或 JSON 格式返回数据。
*
但是请注意,Xml 反序列化技术通常使用反射来填充 DTO 属性,这可能要求可序列化属性具有 setter(即使是私有的)。
一种方法是使用结构并将其传入。它还使使用 class 更容易,因为您只需要声明结构状态变量,更改与 "default" 不同的内容,然后将其传入。
public struct HourlyForecastDataState
{
public DateTime DateTime;
public decimal TemperatureCelcius;
public decimal DewPoint;
public string Condition;
public int ConditionCode;
public int WindSpeed;
public string WindDirection;
public decimal WindDegrees;
public int UltravioletIndex;
public decimal Humidity;
public decimal WindChill;
public int HeatIndex;
public decimal FeelsLike;
public decimal Snow;
}
public class HourlyForecastData
{
public DateTime DateTime { get; private set; }
public decimal TemperatureCelcius { get; private set; }
public decimal DewPoint { get; private set; }
public string Condition { get; private set; }
public int ConditionCode { get; private set; }
public int WindSpeed { get; private set; }
public string WindDirection { get; private set; }
public decimal WindDegrees { get; private set; }
public int UltravioletIndex { get; private set; }
public decimal Humidity { get; private set; }
public decimal WindChill { get; private set; }
public int HeatIndex { get; private set; }
public decimal FeelsLike { get; private set; }
public decimal Snow { get; private set; }
public HourlyForecastData(HourlyForecastDataState state)
{
DateTime = state.dateTime;
TemperatureCelcius = state.temperatureCelcius;
//...etc
}
}
//Usage:
HourlyForecastDataState HFDstate = new HourlyForecastDataState();
HFDstate.temperatureCelcius = 100 //omg, it's hot!
HourlyForecastData HFD = new HourlyForecastData(HFDstate);
在这种情况下,组合可能很合适。特别是因为有一些参数属于特定类别。
例如:
public int WindSpeed;
public string WindDirection;
public decimal WindDegrees;
为它们创建一个新对象,然后访问不同的值:
weatherData.Wind.Speed;
并将新的风对象传递给构造函数:
var wind = new Wind(xmlData.WindSpeed, xmlData.WindDirection, xmldata.WindDegrees);
var weatherReport = new WeatherReport(wind, /* .... */);
我还要介绍几个枚举。因为截至目前,weatherReport
的用户必须知道字符串 WindDirection
可以具有哪些值。如果您将字符串转换为枚举,则使用不同的值会容易得多。
最后要注意的是,我通常仅在确实必须为 class 指定某些值才能具有有效状态时才使用构造函数。例如,在您的情况下,最低有效状态是日期和温度?然后把它们放在构造函数中。
public class HourlyForecastData
{
public DateTime DateTime { get; private set; }
public decimal TemperatureCelcius { get; private set; }
public decimal DewPoint { get; private set; }
public string Condition { get; private set; }
public int ConditionCode { get; private set; }
public int WindSpeed { get; private set; }
public string WindDirection { get; private set; }
public decimal WindDegrees { get; private set; }
public int UltravioletIndex { get; private set; }
public decimal Humidity { get; private set; }
public decimal WindChill { get; private set; }
public int HeatIndex { get; private set; }
public decimal FeelsLike { get; private set; }
public decimal Snow { get; private set; }
public HourlyForecastData(DateTime dateTime, decimal temperatureCelcius, ...)
{
DateTime = dateTime;
TemperatureCelcius = temperatureCelcius;
//...set all the other properties via constructor
}
}
我正在努力学习更好的软件设计和 OOP。我正在创建一个库,该库可以访问以 XML 回复的天气服务。该服务提供了许多不同的字段,因此我为每个 XML 字段创建了属性。但是,通过构造函数设置那么多属性感觉有点乱。我可以省略构造函数并使用 public 设置器,但我正在尝试制作一个不可变的 class.
我为此查看了不同的设计模式,似乎有一些 "Builder" 和 "Factory" 模式。但是,我很难理解如何将其应用到我的代码中。或者我应该使用完全不同的东西来填充这些对象中的属性?
Re 是否有更好的 OOP 方法?
class 上的大量属性通常表明需要拆分 class(SOLID 的 Single Responsibility Principle)。
例如看起来 HourlyForecastData
对风(速度和方向)、降水(雪、露和雨)和温度(最小值、最大值...)建模,这些问题可以拆分为单独的 classes,然后 HourlyForecastData
将是三者的组合。
回复:建造者模式
构建器模式可用于减轻构建大型(通常是不可变的)classes 或图形期间的负担,但显然需要额外的(可变的)Builder class(es) 来构建目标 class 表示(即 HourlyForecastData
)并最终创建它(即,通过将所有参数传递给构造函数来不可变地构造它)。因此,如果那是 'better' 所要求的,那么这并不会减少工作量,但这当然可以更容易阅读,例如:
HourlyForecastData todaysForecast = new HourlyForecastDataBuilder()
.WithBaseline(ObjectMother.WinterSnow) // Provide an archetype
.WithPrecipitation(snow: 5, rain:1) // Dew defaults to 0
.Build();
基线原型/object mothers 如果某个地区的天气模式经常稳定并且只需要进行小幅调整,则将很有用。 IMO 构建器模式在测试中最有用。我看不出明显适合 Xml 序列化用法。
另见 Named and Optional parameters
回复:不变性
私有 setter 在技术上仍然允许可变性,尽管在 class 本身内受到限制。 C#6 及更高版本支持 getter-only auto properties,这是实现不可变属性的最简单形式
public class HourlyForecastData
{
public DateTime DateTime { get; }
...
public HourlyForecastData(DateTime dateTime, ...)
{
// Get only auto properties can only be set at construction time
DateTime = dateTime;
...
不相关,但 Scala offers an even more concise syntax than C# 用于在 class 上定义不可变的 public 属性,方法是在(主)构造函数中声明一次:
class HourlyForecastData(val temperature: Int, val station: String, ...) {
}
不需要任何进一步的 属性 或支持字段,同时表达和执行不变性。但是,调用者仍然需要承担提供所有参数的负担(无论是直接提供,还是通过 Builder 等提供)。
回复:Xml 如果您提供 API,我建议您使用 WebAPI。我建议不要将 Xml 序列化问题构建到您的 DTO classes 中,而是依赖 Content Negotiation。这将允许调用者确定是否应以 Xml 或 JSON 格式返回数据。
*
但是请注意,Xml 反序列化技术通常使用反射来填充 DTO 属性,这可能要求可序列化属性具有 setter(即使是私有的)。
一种方法是使用结构并将其传入。它还使使用 class 更容易,因为您只需要声明结构状态变量,更改与 "default" 不同的内容,然后将其传入。
public struct HourlyForecastDataState
{
public DateTime DateTime;
public decimal TemperatureCelcius;
public decimal DewPoint;
public string Condition;
public int ConditionCode;
public int WindSpeed;
public string WindDirection;
public decimal WindDegrees;
public int UltravioletIndex;
public decimal Humidity;
public decimal WindChill;
public int HeatIndex;
public decimal FeelsLike;
public decimal Snow;
}
public class HourlyForecastData
{
public DateTime DateTime { get; private set; }
public decimal TemperatureCelcius { get; private set; }
public decimal DewPoint { get; private set; }
public string Condition { get; private set; }
public int ConditionCode { get; private set; }
public int WindSpeed { get; private set; }
public string WindDirection { get; private set; }
public decimal WindDegrees { get; private set; }
public int UltravioletIndex { get; private set; }
public decimal Humidity { get; private set; }
public decimal WindChill { get; private set; }
public int HeatIndex { get; private set; }
public decimal FeelsLike { get; private set; }
public decimal Snow { get; private set; }
public HourlyForecastData(HourlyForecastDataState state)
{
DateTime = state.dateTime;
TemperatureCelcius = state.temperatureCelcius;
//...etc
}
}
//Usage:
HourlyForecastDataState HFDstate = new HourlyForecastDataState();
HFDstate.temperatureCelcius = 100 //omg, it's hot!
HourlyForecastData HFD = new HourlyForecastData(HFDstate);
在这种情况下,组合可能很合适。特别是因为有一些参数属于特定类别。
例如:
public int WindSpeed;
public string WindDirection;
public decimal WindDegrees;
为它们创建一个新对象,然后访问不同的值:
weatherData.Wind.Speed;
并将新的风对象传递给构造函数:
var wind = new Wind(xmlData.WindSpeed, xmlData.WindDirection, xmldata.WindDegrees);
var weatherReport = new WeatherReport(wind, /* .... */);
我还要介绍几个枚举。因为截至目前,weatherReport
的用户必须知道字符串 WindDirection
可以具有哪些值。如果您将字符串转换为枚举,则使用不同的值会容易得多。
最后要注意的是,我通常仅在确实必须为 class 指定某些值才能具有有效状态时才使用构造函数。例如,在您的情况下,最低有效状态是日期和温度?然后把它们放在构造函数中。