有没有"权利"抽象出我的代码的方法?

时间:2015-06-26 10:37:51

标签: c# oop

我已经在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;
    }

}

一个例子比另一个更好,如果是这样,为什么?或者有一种完全不同的方式吗?

4 个答案:

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