是否存在“;右“;提取代码的方法

本文关键字:方法 取代码 存在 是否 提取 | 更新日期: 2023-09-27 17:59:47

我已经在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;这样的简单行,我最终会扩充一个庞大的函数。不好!

然而,我看不出以下两个例子有什么问题(我赞成例子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;
    }
}

一个例子比另一个好吗?如果是,为什么?还是有完全不同的方法?

是否存在“;右“;提取代码的方法

您的第一个示例很好

如果以后AlertDateTime需要初始化一个更复杂的函数,那么您总是可以将代码重构为类似示例3的东西。在此之前,请尊重KISS(保持简单)和YAGNI原则。

请注意,接口(公开可用的方法及其签名)在示例1和示例3之间没有变化。这是一件好事。这意味着您可以在这些样式之间移动,而不必修改使用类的代码。

然而,示例2有很多问题:

  • 信息隐藏原则基本上是说,你不应该在没有充分理由的情况下公开揭露一些事情。为什么要将新生成的Alert存储在可公开访问的"全局变量"中?

  • 示例2的行为不同:如果您两次调用GenerateAlert,它将两次返回对相同Alert对象的引用。(想想如果你今天叫一次,明天再叫一次会发生什么。)

顺便说一句,示例3中方法的命名可以得到改进。试着孤立地思考每种方法:PopulateAlertDate()不会填充警报日期。它返回一个日期,该日期可以用于填充警报日期。名称GetDefaultAlertDate()可能更合适。

+1是海因茨的伟大答案。

我将在示例3中添加,您正在使用Façade模式的变体。你正在用复杂的&重复初始化逻辑,还隐藏此对象的接口并公开新方法。如果以后您有几种不同的方法来创建同一个对象,您应该考虑Factory模式

请注意:如果没有理由一次使用另一个变体,您应该首先支持将一些代码放在原始类"构造函数中。

示例2类似于Singleton反模式,它有另一个目的——保留类的一个实例。这通常是针对您喜欢一次性创建的服务。即便如此,您最好查看DependencyContainers以获得更好的单元测试功能。

如果这些函数中有比赋值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 */; }
}

分别。

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>();

您正在尝试实例化一个对象,我认为使用静态方法没有意义(工厂已经有了答案,您真的需要吗?)

在你必须创建这个对象的地方,只需进行

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;
     }
}