有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

需要初始化对象时使用的java设计模式?

我有一个类,它有一个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方法-这看起来也不是一个很好的解决方案,尤其是如果有多个方法的话

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


共 (5) 个答案

  1. # 1 楼答案

    或者另一种解决方案,这里的想法是你打破了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
        }
    }
    
  2. # 2 楼答案

    我建议使用合作者进行初始化。这样,通过用一个mock替换初始化器协作者,就可以轻松地测试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
    }
    

    }

  3. # 3 楼答案

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

    • 正如@andy turner所指出的,静态工厂可用于数据库安装

    • 以及repository pattern对数据库进行工作

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

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

  4. # 4 楼答案

    当我想要一个实例必须初始化一次的类,但我想将初始化推迟到必要的时候(此时调用方可能无法调用初始化函数,发现这样做很不方便,等等),我会像你从代码开始那样做,但我将初始化方法设置为私有,并将其命名为“EnsureInitialized”。如果初始化已经完成,它使用一个标志跟踪并提前退出,所有依赖于已经发生初始化的函数只需调用该函数作为第一行(在参数检查之后)

    如果我希望调用者控制这个实例的初始化何时完成,我会将该方法公开,将其命名为“Init”,跟踪它是否在Init方法内运行过一次,处理幂等性或max run,但是这对该类是合适的,所有依赖于已经运行的Init的方法都会调用不同的,名为“”断言化“的私有方法将抛出一个异常,其中包含“在使用此函数之前必须在{class name}实例上调用init”这样的文本

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

  5. # 5 楼答案

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

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

    此外,我将删除initialized变量——部分原因是您不再需要它了——但也因为它需要一些保证线程安全性的方法(例如同步、volatile或AtomicBoolean

    我认为关于(不)在构造函数中工作的Miško Hevery's博客文章是一篇有趣的阅读