我已经在C#中开发了大约12个月了(从头开始,除了一点点PHP脚本黑客之外没有以前的开发经验)我喜欢认为我已经将我的技能发展到了我可以编写的水平应用程序,它完美地执行其功能。
但是,我仍然对最佳编码实践感到困惑,我知道这段代码很糟糕:class Example1
{
public static Alert GenerateAlert()
{
Alert AlertObject = new Alert();
AlertObject.AlertDatetime = DateTime.Now;
AlertObject.AlertHasRecords = false;
return AlertObject;
}
}
例如,如果AlertDatetime
需要的不仅仅是像DateTime.Now;
这样的简单行,那么我最终会扩大一个庞大的功能。不好!</ p>
但是,我看不出以下两个例子的问题(我赞成例2)
class Example2
{
public static Alert AlertObject = new Alert();
public static Alert GenerateAlert()
{
PopulateAlertDate();
CheckForAlertRecords();
return AlertObject;
}
private static void CheckForAlertRecords()
{
AlertObject.AlertHasRecords = false;
}
private static void PopulateAlertDate()
{
AlertObject.AlertDatetime = DateTime.Now;
}
}
class Example3
{
public static Alert GenerateAlert()
{
Alert AlertObject = new Alert();
AlertObject.AlertDatetime = PopulateAlertDate();
AlertObject.AlertHasRecords = CheckForAlertRecords();
return AlertObject;
}
private static bool CheckForAlertRecords()
{
return false;
}
private static DateTime PopulateAlertDate()
{
return DateTime.Now;
}
}
一个例子比另一个更好,如果是这样,为什么?或者有一种完全不同的方式吗?
答案 0 :(得分:7)
你的第一个例子很好。
如果稍后AlertDateTime
需要更复杂的函数进行初始化,您可以重构您的代码,例如示例3.在此之前,尊重KISS(保持简单)和YAGNI原则。
请注意,接口(公开可用的方法及其签名)在示例1和示例3之间不会发生变化。这是一件好事。这意味着您可以在这些样式之间移动,而无需修改使用您的类的代码。
然而,示例2存在很多问题:
information hiding principle基本上表示你不应该在没有充分理由的情况下公开曝光。为什么要将新生成的警报存储在可公开访问的&#34;全局变量&#34;?
示例2 行为的方式不同:如果您两次调用GenerateAlert
,它将同时返回对相同警告对象的引用。 (想想如果你明天再打一次会发生什么。)
作为旁注,可以改进示例3中方法的命名。尝试单独考虑每个方法:PopulateAlertDate()
不填充警报日期。它返回一个可以使用的日期来填充警报日期。名称GetDefaultAlertDate()
可能更合适。
答案 1 :(得分:1)
+1为Heinzi的伟大答案。
我将在示例3中添加您使用Façade模式的变体。你正在用一个复杂的&amp;重复初始化逻辑,并隐藏此对象的接口并显示新方法。如果稍后您有几种不同的方法来创建同一个对象,则应考虑工厂模式。
注意:如果没有理由一次使用其他变体,首先应该支持将一些代码放在原始类“构造函数中”。
示例2类似于 Singleton反模式,它有另一个目的 - 保留一个类的实例。这通常适用于您希望一劳永逸地创建的服务。即便如此,您最好还是查看依赖容器以获得更强大的单元测试功能。
答案 2 :(得分:0)
如果这些功能中的逻辑比仅指定true或false更多,则可能需要使用工厂和接口。遵循可靠原则的完全抽象的代码如下所示:
public class AlertFactory : IAlertFactory {
IAlertDatePopulator alertDatePopulator;
IAlertRecordsChecker alertRecordsChecker;
public AlertFactory(IAlertDatePopulator alertDatePopulator, IAlertRecordsChecker alertRecordsChecker) {
this.alertDatePopulator= alertDatePopulator;
this.alertRecordsChecker = alertRecordsChecker;
}
public Alert GenerateAlert() {
Alert alertObject = new Alert();
alertObject.AlertDatetime = alertDatePopulator.Populate();
alertObject.AlertHasRecords = alertRecordsChecker.Check();
return alertObject;
}
}
与
interface IAlertFactory { Alert GenerateAlert(); }
interface IAlertDatePopulator { DateTime Populate(); }
interface IAlertRecordsChecker { bool Check(); }
然后,您可以为这些接口添加具体实现,例如:
public class DateTimeNowAlertDatePopulator : IAlertDatePopulator {
public DateTime Populate() { return DateTime.Now; }
}
public class SomeCalculationAlertDatePopulator : IAlertDatePopulator {
public DateTime Populate() { return /* something calculated */; }
}
RESP。
public class AlwaysFalseAlertRecordsChecker : IAlertRecordsChecker {
public bool Check() { return false; }
}
public class SomeCalculationAlertRecordsChecker : IAlertRecordsChecker {
public bool Check() { return /* something calculated */; }
}
然后您可以创建已配置的工厂:
public class DateNowAndRecordsFalseAlertFactory : AlertFactory {
public DateNowAndRecordsFalseAlertFactory ()
: base (new DateTimeNowAlertDatePopulator(), new AlwaysFalseAlertRecordsChecker()) { }
}
public class DateNowAndCalculatedRecordsAlertFactory : AlertFactory {
public DateNowAndCalculatedRecordsAlertFactory ()
: base (new SomeCalculationAlertDatePopulator(), new AlwaysFalseAlertRecordsChecker()) { }
}
然后只使用你的工厂:
var alertFactory = new DateNowAndRecordsFalseAlertFactory ();
var myAlert1 = alertFactory.GenerateAlert();
var alertFactory2 = new DateNowAndCalculatedRecordsAlertFactory();
var myAlert2 = alertFactory2.GenerateAlert();
等。对于一个简单的功能来说,这似乎有很多代码,但如果你期望有很多逻辑出现的扩展,那么这是遵循开放/关闭原则的干净代码(为扩展打开(通过添加新的接口实现) )但因修改而关闭(不再需要修改现有代码))。
与依赖注入一起使用时最有效。然后,您可以像这样配置工厂:
public class DateNowAndRecordsFalseAlertFactory : AlertFactory {
public DateNowAndRecordsFalseAlertFactory (DateTimeNowAlertDatePopulator alertDatePopulator, AlwaysFalseAlertRecordsChecker alertRecordsChecker)
: base (alertDatePopulator, alertRecordsChecker) { }
}
只是这样做:
var alertFactory = someDiContainer.Resolve<DateNowAndRecordsFalseAlertFactory>();
答案 3 :(得分:0)
您正在尝试实例化一个对象而我没有注意到有一个静态方法(工厂已有答案,您真的需要吗?)
在必须创建此对象的位置,只需执行
var alert = new Alert();
如果要在对象创建后使用默认值自定义某些属性,则此处为快捷方式
var anotherAlert = new Alert() { AlertDatetime = DateTime.Now };
通常你应该以最多可用的方式创建对象的实例,所以如果总是必须用当前日期构造它,这就是构造函数通常做的事情:
public class Alert
{
// do not add class name to property
public DateTime DateTime {get; set;}
// this don't need initialization if default value is false
public bool HasRecords {get; set;}
public Alert()
{
DateTime = DateTime.Now;
}
}