Methods.class中的代码重用与策略模式和依赖注入

时间:2013-06-26 08:37:30

标签: java class unit-testing design-patterns dependency-injection

Status: Fendy和Glen Best的答案同样可以接受并受到我的尊重,但由于我可以接受并获得赏金,我选择了Fendy的答案

Scenario:

如果我某些代码 多次重复使用 很多类(很少有明显的次要参数更改)和并发线程,采用哪种方法?

必须重用的代码可以是任何理智的东西(适当考虑静态和非静态上下文和方法制作技术)。它可以是一个算法,一个DB方法做连接,操作,关闭。任何东西。

  1. 制作一些类MyMethods.class将所有这些方法放入其中

    1.A。使方法 static并直接调用(由所有类和并发线程)MyMethods.someMethod();

    1.B。在<{>}

  2. 使用https://en.wikipedia.org/wiki/Strategy_pattern中所述的策略模式(此处附带的代码)。

  3. 使用https://en.wikipedia.org/wiki/Dependency_injection#Java

  4. 中所述的依赖注入

    non-static

    1. 有些人会说单元测试 http://en.wikipedia.org/wiki/Unit_testing 不可能这种做法会让在换出时遇到麻烦后者。如果您想测试您的课程并使用依赖模拟版

      1.A。 并发调用或多个类会有任何问题吗?特别是在instantiate只是一个例子?

      1.B。我认为这会产生太多的内存负载,因为整个类多次MyMethods mm = MyMethods(); mm.someMethod();只需调用一两个方法

    2. 这是我的头脑,解释那个和/或任何优点/缺点

    3. 想要在上下文中使用框架来解决这个问题。这就是我的想法,请解释一下这个问题和/或任何优点/缺点

    4. 等待任何其他策略或建议(如果有)。

    5. Problems:请回答您是否有经验并深入了解其含义,并能全面地了解您的答案,帮助我和整个社区!

      JDBC static methods

      instanticated

3 个答案:

答案 0 :(得分:8)

你的问题实际上有两个含义。

  

必须在许多类中多次重复使用

它可以是设计模式(可重用组件)或内存开销(类实例化)的上下文。从两个不同的角度讲话:

内存成本(我对此没什么经验,但让我分享一下经验)

此部分实际上仅涵盖了2种实例化。

首先是静态(或组合根中的DI实例化)

  • Eager instantiation,表示在应用程序启动时将实例化所有类
  • 仅限一次实例化

<强>非静态

  • 懒惰实例化,意味着只有在需要时才会实例化
  • 每次使用一次实例化

简而言之,如果类很多,静态将花费很高,如果请求很高,静态将花费很高(例如,在for循环内)。但它不应该使你的应用程序繁重。 java / csharp中的大多数操作都是创建对象。

类可重用性

1 - 巨型单片代码(一个上帝级别能够做几乎所有事情)

优点:

  1. 轻松搜索代码(仍然依赖),你知道每个逻辑都在那里,所以你只需要看看那个大类
  2. 如果它是静态的,您可以在任何地方调用它而不必担心实例化
  3. 缺点:

    1. 对一种方法的任何修改都会在其他地方造成错误风险
    2. 违反SRP,意味着可以通过各种原因更改此类,而不仅仅是一个
    3. 特别是在版本控制中,如果修改发生在分离的分支中,则更难合并,从而导致同步代码的努力
    4. 1a /静态类/单例模式

      优点:

      1. 易于使用
      2. 可以在任何地方使用(只需参考和调用,并完成)
      3. 不需要实例化对象
      4. 缺点:

        1. 难以进行单元测试(很难进行模拟,在后期,您会发现需要时间来准备测试环境。特别是对于数据
        2. 如果有状态(有状态),在调试期间很难确定当前状态。而且,很难确定哪个功能改变了状态,可以从任何地方改变
        3. 倾向于有很多参数(可能大约5-11)
        4. 关于静态类的一些观点:see this question

          2策略模式

          实际上这与3或composition over inheritance具有相同的设计。

          3依赖注入

          优点:

          • 易于模拟和单元测试
          • Must be stateless。如果类是无状态的,则更容易调试和单元测试
          • 支持重构

          缺点:

          • 很难为不熟悉接口的人调试(每次重定向到方法时,都会转到接口)
          • 创建将导致映射的分层

          州/无国籍

          我认为各州在您的应用程序设计中起着重要作用。通常开发人员会尝试避免在业务逻辑代码中使用状态,例如:

          // get data
          if(request.IsDraft){
            // step 1
            // step 2
          }
          else{
            // step 1
            // step 3
          }
          

          开发人员倾向于将逻辑放在其他stateless类中,或者至少包括以下方法:

          // get data
          if(request.IsDraft){
              draftRequestHandler.Modify(request);
          }
          else{
              publishedRequestHandler.Modify(request);
          }
          

          它将提供更好的可读性,并且更容易进行修改和单元测试。还有一种设计模式state pattern or hierarchial state machine pattern,特别是处理这种情况。

          单一责任原则

          恕我直言,如果遵循这个原则是最有利的。优点是:

          1. 在版本控制中,更改清楚了哪个类已被修改以及为什么
          2. 在DI中,可以连接几个较小的类,在使用和单元测试方面创造灵活性
          3. 增加模块化,低耦合和高内聚
          4. TDD(测试驱动开发)

            此设计不保证您的代码没有错误。考虑到在设计阶段和分层工作中花费时间,它具有以下好处:

            1. 易于模拟对象进行单元测试
            2. 由于单元测试和模块化而易于重构
            3. 易于维护/扩展
            4. 一些有用的资源

              Service Locator Anti Pattern

              Using Decorator for cross-cutting concern

              我的2美分:

              The benefit of using interface (also apply for composition ofer inheritance)

              Doing top down design / DI design

              最后的想法

              这些设计和策略不是决定您的应用程序结构的关键。它仍然是决定它的建筑师。我更喜欢遵循一些原则,如SOLID,KISS和GRASP,而不是决定什么是最好的结构。据说依赖注入遵循这些原则中的大多数原则,但过多的抽象和不正确的组件设计会导致单独模式的误用。

答案 1 :(得分:3)

  

1.A。使方法静态并直接调用(由所有类和并发线程)作为MyMethods.someMethod();

     

1.B。使方法非静态并且在调用它们时,通过MyMethods mm = MyMethods()实例化整个类; mm.someMethod();

这两者之间的选择取决于MyMethods.class的功能。如果MyMethods应该是无状态的,那么采用static方法是一种很好的方法。否则,如果一个方法调用依赖于另一个,MyMethods具有状态(即非最终字段),则使用第二个选项。

  

使用https://en.wikipedia.org/wiki/Strategy_pattern中所述的策略模式(此处随附的代码)。

如果要为不同目的使用不同的类扩展MyMethods,并且根据您的上下文选择要运行的代码,请使用此模式。正如 wiki 所说,如果您在运行时之前不知道要使用的算法(取决于某些条件),这就是要走的路。根据您的MyMethods规范,您没有这样的问题。

  

使用https://en.wikipedia.org/wiki/Dependency_injection#Java

中所述的依赖注入

与上述答案相同。 依赖注入的内容是控制反转。使用MyMethods的类不知道MyMethods的实际实现,但实际实现的注入被委托给某个更高级别的权限。它从将要使用它的上下文中抽象出外部依赖项。同样,如果MyMethods是无状态和常量(没有计划改变,并且类中的方法的行为不依赖于它们被使用的上下文),你不需要这些模式,因为它只是意味着过度工程。

如果MyMethods的逻辑取决于运行它们的上下文,我会得出结论,你应该使用策略 DI 模式。如果这是不变的(例如,Java的Math类不关心谁或者在什么情况下有人调用sqrt()max()pow())那么静态方法就是可行的方法。


关于问题:

使用MyMethods static方法时,您所说的问题不存在。您将不得不测试您的方法是否为特定参数返回正确的值,就是这样。我不相信在策略模式中测试Strategy的实际实现或通过依赖注入注入的接口实现会有更多麻烦。可能更难的是测试使用策略的类,因为有时候重新创建特定策略将被执行的上下文并不容易,因为它通常取决于用户输入,但这绝对不是不可能的。就我而言,依赖注入非常适合测试,因为你可以将测试中的单元与可以轻松模拟的依赖项分开。

答案 2 :(得分:3)

  1. 主要问题:代码重用

      

    如果我有一些代码必须在很多类中重复使用很多次(很少有明显的次要参数更改)和并发线程,那么采用哪种方法?

    因为你没有考虑任何削减和口交,我认为你的意思是:

      

    ......许多班级多次重复使用......

    你提出的问题并不特别或具体。代码在单个应用内或多个应用中重复使用是很常见的。 常见答案:使用面向对象的设计/编程。将代码放在一个类中,创建一个对象作为实例,调用该对象......

    1a上。通过静态方法重用:

      

    使方法静态并直接调用(由所有类和并发线程)MyMethods.someMethod()

    • 如果你的班级是无国籍的(没有实例变量),这是一个很好的方法。
    • 如果您的类具有类级别状态(仅限静态实例变量),但变量是只读的(不可变),这是一种很好的方法。
    • 如果您的类具有类级别状态(仅限静态实例变量)并且变量更改值(可变),那么这可能是一种合适的方法。但是,如果您希望可以从多个线程访问您的类,则必须使其成为线程安全的:使您的方法同步,或者使内部代码同步(互斥线程访问)所有数据读写。
    • 如果您的代码具有对象级状态(非静态实例变量),则此方法无法工作 - 无法在不实例化对象的情况下访问非静态实例变量。

    1b中。通过非静态方法重用,使用对象实例化:

      

    使方法非静态并且在调用它们时,通过MyMethods实例化整个类mm = MyMethods(); mm.someMethod();

    • 如果你的类只有静态实例变量,这是一个糟糕的方法,因为实例化对象什么也没有实现
    • 如果您的类具有非静态实例变量 - 这是唯一的方法。必须实例化一个对象来访问变量。
    • 如果跨多个线程使用实例化对象,则它们应该(按优先顺序):
      • 无状态(无实例变量) - 实际上是1a的选项 - 无需实例化
      • 不可变(只读非静态实例变量)
      • 同步所有数据读取&amp;写入
  2. 使用策略模式

    策略模式可以是良好的实践。但它与你的整体问题没什么关系。策略模式用于特定原因 - 交换算法/处理逻辑的实现&#34;即时&#34;不影响来电者。

  3. 使用依赖注入

    出于以下原因使用依赖注入:

    • 工厂&amp;对象缓存功能:从代码中删除对象创建,缓存和查找的责任
    • 对象共享的中介:允许各种类共享同一个对象实例(存储在给定的范围/上下文中),而不是两个类直接在它们之间传递对象
    • &#34;接线控制&#34;在对象实例之间 - 设置对象关联,在CDI下,支持拦截器,装饰器和观察者模式

    如果使用得当,这可能是非常好的做法。在您的情况下,这只能在选项1b下适用。依赖注入是关于对象实例化和提供变量的。

  4. 问题:

    1. 有些人会说单位测试不可能

      • 模拟框架(和手工编码单元测试)处理用模拟逻辑替换类,一直。这是一个非常正常的场景。你可以扩展一个类来模拟它的逻辑 - 如果它没有最终的公共方法。此外,您可以将方法声明传递给接口,让类实现接口,然后通过使用不同的类实现接口来进行模拟。
      • 换句话说,这不是影响任何选项的约束/力量

      1a上。见上文

      1b中。内存负载

        

      我认为它会产生太多的内存负载,因为整个类只需调用一两个方法即可多次即时显示

      一个小问题。根据每个对象实例(实例变量)中的数据,每个对象实例可以小到十几个字节或大到兆字节 - 但通常倾向于低端(通常<1kB)。每次实例化类时,都不会复制类代码本身的内存消耗。

      当然,根据您的要求,最大限度地减少对象的数量是一种很好的做法 - 如果您已有可用的实例,请不要创建新的实例。创建更少的对象实例并在整个应用程序中共享它们 - 将它们传递给构造函数方法和setter方法。依赖注入是一种自动共享对象实例的好方法。没有将它们传递给构造函数/ setter。

    2. 见上文

    3. 见上文