【问题标题】:Design pattern to use when you need to initialize your object?需要初始化对象时使用的设计模式?
【发布时间】:2016-08-19 00:07:23
【问题描述】:

我有一个类,它有一个 Initialize 方法,它在数据库中创建一堆表。这个类看起来像这样:

public class MyClass
{
  private bool initialized = false;

  public void Initialize()
  {
    if(!initialized)
    {
        //Install Database tables
        initialized = true;
    }
  }

  public void DoSomething()
  {
    //Some code which depends on the database tables being created 
  }

  public void DoSomethingElse()
  {
    //Some other code which depends on the database tables being created 
  }
} 

DoSomething 和 DoSomethingElse 这两个方法需要确保在继续之前已调用 Initialize 方法,因为它们依赖于数据库中的表。我有两个选择:

  1. 在类的构造函数中调用 Initialize 方法 - 这似乎不是一个好主意,因为构造函数现在应该调用方法,这些方法很重要并且可能导致异常。

  2. 在这两个方法中的每一个中调用 Initialize 方法 - 这似乎也不是一个很好的解决方案,尤其是在有多个方法时。

有没有一种设计模式可以更优雅地解决这个问题?

【问题讨论】:

    标签: java c# oop design-patterns


    【解决方案1】:

    当我想要一个类,其实例必须只初始化一次,但我想将初始化推迟到必要之前(此时调用者可能无法调用 Initialize 函数,发现这样做不方便,等等。 ),我的做法与您开始编写代码的方式类似,但我将初始化方法设为私有并将其命名为 "EnsureInitialized"。如果初始化已经完成,它使用一个标志来跟踪和提前退出,并且所有依赖于初始化的函数都只是调用该函数作为它们的第一行(在参数检查之后)。

    如果我希望调用者控制该实例的初始化何时完成,我将方法公开,将其命名为“Init”,跟踪它是否已使用标志运行,在 Init 中处理幂等性或 max-run-once但是,该方法适用于该类,并且所有依赖于已运行的 Init 的方法都将调用名为 "AssertIsInitialized" 的不同私有方法,该方法将引发异常,并带有诸如“必须调用 init on”之类的文本{class name} 使用此函数之前的实例”。

    我对这些不同模式的目标是在类实例生命周期内明确每个方法的期望和关于初始化的操作,并提供可发现性(使用它的设计或代码错误)和自动行为(在 self 的情况下) -在我的第一段中初始化类)我认为每个最适合应用程序的其余部分的地方。

    【讨论】:

      【解决方案2】:

      或者另一种解决方案,这里的想法是你打破了 MyClass 中的单一责任原则。在同一类中的这些表上存在非平凡的初始化行为(安装数据库表)和行为。因此,您应该将这些职责分成两个不同的类,并将一个作为协作者传递给另一个。

      public class MyClass {
      
          DatabaseCollaborator collaborator;
      
          public MyClass(DatabaseCollaborator collaborator) {
              this.collaborator = collaborator;
          }
      
          public void DoSomething() {
              //Some code which depends on the database tables being created
              collaborator.someMethod();
          }
      
          public void DoSomethingElse() {
              //Some other code which depends on the database tables being created
              collaborator.anotherMethod();
          }
      
      }
      
      public class DatabaseCollaborator {
      
          DatabaseConfig config;
      
          public DatabaseCollaborator(DatabaseConfig config) {
              this.config = config;
          }
      
          public void someMethod() {
      
          }
      
          public void anotherMethod() {
      
          }
      }
      
      public class DatabaseConfig {
      
          public DatabaseConfig() {
              // initialize
          }
      }
      

      【讨论】:

        【解决方案3】:

        我建议使用进行初始化的协作者。这样,MyClass 可以很容易地通过用模拟来代替初始化协作者进行测试。例如:

        public class MyClass {
        
        public MyClass(MyClassInitialiser initialiser) {
            initialiser.initialize();
        }
        
        public void DoSomething() {
            //Some code which depends on the database tables being created
        }
        
        public void DoSomethingElse() {
            //Some other code which depends on the database tables being created
        }
        

        }

        【讨论】:

          【解决方案4】:

          我会将数据库的安装与依赖它的任务定义分开:

          • @andy-turner 指出,静态工厂可用于数据库安装

          • repository pattern 处理数据库

          我建议这个解决方案,因为如果我理解正确,您会担心依赖于数据库的大量任务。

          使用依赖注入模式,存储库可以获得对数据库的引用,因此在您的引导代码中,您可以执行一次数据库安装,然后在依赖它的所有存储库中注入对数据库的引用。

          【讨论】:

            【解决方案5】:

            我会使用 static factory method 来调用 Initialize,并将构造函数设为私有,以强制使用静态工厂方法:

            public class MyClass
            {
              private MyClass() { ... }
            
              public static MyClass createInstance() {
                MyClass instance = new MyClass();
                instance.Initialize();
                return instance;
              }
            }
            

            另外,我会删除initialized 变量——部分原因是你不再需要它——但也因为它需要一些方法来保证线程安全的可见性(例如同步、易失性或AtomicBoolean)。

            我认为 Miško Hevery's 关于(不)在构造函数中工作的博文很有趣。

            【讨论】:

            • 所以如果我多次调用 MyClass.createInstance() 数据库会被多次初始化?
            • @DaveH 这完全取决于你如何实现Initialize
            • @DaveH 如果你想防止你的类被多次初始化,你可以使用单例设计模式。
            • @Carl 没有特别要求使用单例。例如,您可以使用代理;那么它的多样性就没有必要的限制了。
            • @AndyTurner 出于好奇,在这种情况下使用代理的目的是什么?
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-06-27
            • 2019-07-23
            • 2012-09-01
            • 1970-01-01
            • 2014-10-15
            • 1970-01-01
            相关资源
            最近更新 更多