代码之家  ›  专栏  ›  技术社区  ›  Anonymous

对这门课的建设性批评

  •  4
  • Anonymous  · 技术社区  · 15 年前

    我刚刚查看了一些类似于此的代码

    public class ProductChecker
    {
         // some std stuff
         public ProductChecker(int AccountNumber)
         {
             var account = new AccountPersonalDetails(AccountNumber);
             //Get some info from account and populate class fields
         } 
         public bool ProductACriteriaPassed()
         {
              //return some criteria based on stuff in account class 
              //but now accessible in private fields
         }
    
    }
    

    现在已经添加了一些额外的条件,这些条件不需要AccountPersonalDetails类中的数据。

    新代码如下

    public class ProductChecker
    {
         // some std stuff
         public ProductChecker(int AccountNumber)
         {
             var account = new AccountPersonalDetails(AccountNumber);
             var otherinfo = getOtherInfo(AccountNumber)
             //Get some info from account and populate class fields
         } 
         public bool ProductACriteriaPassed()
         {
              //return some criteria based on stuff in account class  
              // but now accessible in private fields and other info
         }
    
         public otherinfo getOtherInfo(int AccountNumber)
         {
            //DIRECT CALL TO DB TO GET OTHERINFO 
         }
    
    }
    

    我被数据库部分困扰,但人们能告诉我为什么这是错误的吗?或者是?

    7 回复  |  直到 15 年前
        1
  •  6
  •   Alex Martelli    15 年前

    在系统的分层视图中,它看起来像 ProductChecker 属于业务规则/业务逻辑层,因此不应“污染”任何一个用户交互功能(属于该层) 在上面 ) --这与您的案例密切相关——存储功能(属于层中) 在下面 )。

    对于存储层,“其他信息”应该封装在自己的类中,并且该类应该是处理持久性/检索功能的类(就像我想象的那样 AccountPersonalDetails 做自己的事)。“个人信息”和“其他信息”是最好作为单独的类保存还是合并到一个类中,我无法从所提供的信息中分辨出来,但是应该仔细考虑和权衡选项。

    保持层分离的经验法则有时可能会让人感觉很僵化,而且它经常会让人倾向于通过层的混合来添加一个特性——但是为了在系统增长时保持系统的可维护性和清洁性,每当出现这样的设计问题时,我几乎总是主张层分离。在OOP术语中,它表示“强内聚但弱耦合”;但在某种意义上,它比OOP更为基本,因为它也适用于其他编程范式及其混合!-)

        2
  •  1
  •   Sarah Vessels    15 年前

    似乎是额外的数据 getOtherInfo 应封装为 AccountPersonalDetails 类,因此已经是 account 当您创建 new AccountPersonalDetails 对象。你通过 AccountNumber 两个都有,为什么不 会计人员详细信息 聚集 全部的 你需要的信息?那么你就不必像现在这样在外部附加额外的东西了。

        3
  •  1
  •   Justin Niessner    15 年前

    显然,在类的设计中可能会有一些问题……但如果不知道应用程序的完整体系结构,就很难判断出来。

    首先,如果OtherInfo对象与帐户而不是您正在检查的产品相关,那么它将为您的类引入不应该存在的职责。

    其次,如果您有一个数据访问层……那么ProductChecker类应该使用数据访问层从数据库中检索数据,而不是直接调用来检索所需的数据。

    第三,我不确定getotherinfo方法是否需要公开。它看起来像一些只应该在类内部使用的东西(实际上,如果它从一开始就属于那里的话)。在这种情况下,您也不需要传递accountID(您的类应该在某个地方保存它)。

    但是…如果OtherInfo与您正在检查的产品相关,并且您没有真正的数据访问层,那么我可以看到这可能是一个有效的设计。

    不过,我还是站在你这边。我不喜欢。

        4
  •  0
  •   tster    15 年前

    考虑到一个accountnumber被传递到构造函数中,您不必将它传递给类似的另一个方法。

        5
  •  0
  •   Chris S    15 年前

    几点

    • 参数名是pascal大小写,而不是camel(这可能是一个错误)
    • getOtherInfo() 看来这是 AccountPersonalDetails 在那个班也应该如此
    • 您可能希望使用fa_ade类或存储库模式来检索 会计人员详细信息 而不是使用构造函数
    • GoToeIn() 也可能与此重构相关,因此数据库逻辑不嵌入到域对象中,而是嵌入到服务类(fa_ade/repository)中。
    • ProductACriteriaPassed() 在正确的地方
        6
  •  0
  •   queen3    15 年前

    我建议这样做:

    public class AccountPersonalDetails
    {
       public OtherInfo OtherInfo { get; private set; }
    }
    
    public class ProductChecker
    {
         public ProductChecker(AccountPersonalDetails) {}
    }
    
    // and here's the important piece
    public class EitherServiceOrRepository
    {
       public static AccountPersonalDetails GetAccountDetailsByNumber(int accountNumber)
       {
           // access db here
       }
    
       // you may also feel like a bit more convinience via helpers
       // this may be inside ProductCheckerService, though
       public static ProductChecker GetProductChecker(int accountNumber)
       {
          return new ProductChecker(GetAccountDetailsByNumber(accountNumber));
       }
    }
    

    我不是领域驱动设计专家,但我相信这就是DDD的意义所在。您可以保持您的逻辑不受数据库问题的影响,将其转移到外部服务/存储库。如果有人纠正了我,我会很高兴的。

        7
  •  0
  •   Precipitous    15 年前

    有什么好的。看起来您有一个目标明确的ProductChecker。检查产品。您需要重构或更改此项。如果你不需要的话,你不会的。这是我可能会做的。

    为每个帐号创建一个新的类实例“感觉”很笨拙。构造函数参数应该是类正确行为所必需的。它是类的参数,而不是依赖项。它导致临时工在构造函数中做了大量的工作。类的用法应如下所示:

    result = new ProductChecker().ProductACriteriaPassed(accountNumber)
    

    我会很快重新命名以表明它确实有效。

    result = new ProductChecker().PassesProductACriteria(accountNumber)
    

    其他一些人提到,您可能需要拆分数据库逻辑。如果你想要快速的单元测试,你就需要这样做。大多数程序都需要单元测试(除非你只是随便玩),如果速度快的话,它们就更好了。当你能把数据库排除在外的时候,它们是快速的。

    让我们创建一个表示数据库结果的虚拟对象,并将其传递给一个确定产品是否通过的方法。如果不是为了证明,这将是一个私人的。测试性获胜。假设我想验证一个规则,比如“如果帐号是prime,产品必须是绿色的。”这种单元测试方法在没有复杂基础设施的情况下非常有效。

    // Maybe this is just a number of items. 
    DataRequiredToEvaluateProduct data =  // Fill in data
    // Yes, the next method call could be static. 
    result = new ProductChecker().CheckCriteria(accountNumber, data)
    // Assert result
    

    现在我们需要连接数据库。数据库是一个依赖项,它是类正确运行所必需的。它应该在构造函数中提供。

    public class ProductRepository {} // Define data access here. 
    
    // Use the ProductChecker as follows. 
    result = new ProductChecker(new ProductRepository()).CheckCriteria(accountNumber)
    

    如果构造器变得非常冗长(可能需要读取配置文件才能找到数据库),请创建一个工厂来为您进行排序。

    result = ProductCheckerFactory().GimmeProductChecker().CheckCriteria(accountNumber)
    

    到目前为止,我还没有使用任何基础设施代码。通常,我们会通过模拟和依赖注入(我使用 rhinomocks autofac )我不想谈这个。如果你已经准备好了,那就更容易了。