【问题标题】:Passing this to a constructor将此传递给构造函数
【发布时间】:2012-01-15 12:58:26
【问题描述】:

在我的大多数 Java 应用程序中,我都有一个控制器(逻辑)类和一个 GUI 类。我通常需要 Logic 类来调用 GUI 类上的方法,反之亦然。现在,当 Logic 构建 GUI 时,我在构造函数中传递对“this”的引用,以便 GUI 可以调用 Logic 上的方法。这是一些示例代码。

public class Logic {
   private int num = 2;
   private final GUI gui;

   public Logic(){
       gui = new GUI(this);
   }

   public int getNum(){
      return num;
   }
}


public class GUI {
   private final Logic logic;

   public GUI(Logic logic){
      this.logic = logic;
   }

   public void calledLater(){
      int num = logic.getNum();
   }
}

我的问题是:这是创建聚合关系的最佳 OO 方式,还是我搞砸了我的设计?

【问题讨论】:

标签: java oop constructor this aggregation


【解决方案1】:

在您发布的设计中,您的 UI 和模型之间存在紧密耦合。如果构建大型/正式/严重/长期存在的系统,这有点令人不悦。有一种众所周知的 UI/Model 架构架构模式,称为 Model-View-Controller,可以解决这个问题。

您是否需要为制作 MVC 风格的架构而烦恼,这实际上取决于您的应用程序的上下文以及您未来对它的期望。但是您应该阅读 MVC 并知道它就在那里。

顺便说一句,我在您的代码中看到 "code smell":两个对象 A 和 B,每个对象都持有对彼此的引用。通常有一种比循环引用更理想的做事方式。例如,您可以通过让对象 A 持有对 B 的引用(甚至可能“拥有”它)和 B 通过通知或观察者模式等将感兴趣的数据推送给 A 来避免这种情况。这避免了 B 不得不有关于 A 的任何具体信息,甚至必须知道它的存在。瞧,您的设计中的耦合更少,因此您最终有更多的代码重用空间,以及更容易更改的不那么脆弱的系统。

Cocoa(和 Cocoa Touch)中一个重要的核心设计模式是委托模式。这种模式涉及使用协议(类似于 Java 等其他语言中的接口)来解耦信息、事件等的生产者和消费者。

我建议阅读来自 Apple 的 this excellent reference,了解 Cocoa Touch 中的基本设计模式。

【讨论】:

    【解决方案2】:

    将this 传递给UI 类本身不是问题,只要不通过this 调用该类的方法,如您的示例所示。您在GUI 的构造函数中直接调用getNum,应该避免这种情况:您的Logic 类可能还没有准备好返回正确的结果。

    需要注意的一点是,您将 GUI 耦合到 Logic 类,这可能耦合过多。您应该考虑从Logic 中提取一个接口,并让GUI 只与预期的内容交互:​​

    interface DataSourceForTheGui {
        int getNum();
    }
    
    class Logic implements DataSourceForTheGui {
        int getNum() {
            return n;
        }
    }
    
    class GUI {
        private readonly DataSourceForTheGui data;
        public GUI(DataSourceForTheGui data) {
            this.data = data;
        }
        public void display() {
            int k = data.getNum();
        }
    }
    

    【讨论】:

    • 谢谢。 getNum() 只是一个不好的例子。我会删除。但是界面的想法很好,以后会用的。我不确定是否要为小型应用程序(例如我现在正在开发的应用程序)采用完整的 MVC 设计。
    【解决方案3】:

    嗯...我对持有 Logic 实例的 GUI 没意见。毕竟,如果 GUI 应该使用 Logic 对象,它就必须以某种方式访问​​它。

    但是,我宁愿看到 Logic NOT 构造 GUI。如果您真的希望模型和视图分开,我认为您将在某处构造逻辑,并使用逻辑对象构造 GUI。然后,如果 Logic 出于某种原因需要更新 GUI,您应该让它生成一个事件。

    在这种情况下,Logic 需要有一个事件侦听器列表,以便每当它生成事件时,都会通知所有事件侦听器。 GUI 然后向 Logic 注册为侦听器,因此能够接收所有事件。

    一些很好的类是 java.util.EventListener 和 java.util.EventObject。

    但实际上,只有在 Logic 需要异步与 GUI 通信时才需要这样做。否则,请坚持使用 Logic,而不是构建 GUI。

    【讨论】:

      【解决方案4】:

      最好的(或至少最容易理解的)是将初始事物创建为外部类(由主类或主类本身直接调用)。所以你的代码看起来有点像这样。

      public class MainClass{
      
      public static void main(String[] args){
      l = new Logic();
      g = new GUI(l);
      l.setGUI(g);
      }
      
      public static Logic l;
      public static GUI g;
      
      }
      

      【讨论】:

      • 我喜欢这个主意。我想我会这样做,虽然我会为两者都使用一个 setter,而不是在构造函数中传递 l。
      • “逻辑”部分了解 gui 并不是一个好主意。 GUI 应该是一个独立于逻辑的关注点——这两件事在理想情况下不应该如此紧密耦合。这就是 MVC 中的“控制器”派上用场的地方。
      • 我误用了“逻辑”这个词吗?我试图将它与 Controller 同义地使用。在我的大多数应用程序中,Logic 包含 GUI 和 Workers。 Worker 进行繁重的计算,而 Logic/Controller 促进了通信。
      【解决方案5】:

      我会说它会起作用,但我永远不会从您的业务逻辑中引用 GUI 对象。

      • 我会用 cmets 回答。你想做什么?
      • 您的用户应指示您的 GUI,GUI 应正确指示逻辑执行其需要执行的操作,并将结果返回给 GUI。

      其他几个cmets:

      • 我会将您的 Logic num 成员设为常量,因为它不会改变
      • 如果您不为您的 GUI 更改逻辑对象,则没有理由将其传递给您的构造函数。
      • 如果您想更改 Logic 对象,我会在您的 GUI 类中创建一个名为 Logic 的属性,以便您可以访问它并在必要时进行更改。
      • 如果您真的想要花哨,您可以将数据访问层(业务逻辑)保存在与 GUI 不同的库中,这样您的所有界面/应用程序都可以使用它。然后,您可以将 Logic 库导入到任何需要它的接口中。

      【讨论】:

      • 数字只是一个例子。如果我不打算改变它,它将是恒定的。在项目符号 #2 上,我如何能够访问对逻辑的引用而不将其传递给构造函数?
      • GUI g = new GUI(); // 构造函数中没有参数 g.Logic.doSomething(); //如果 Logic 是一个公共属性...但同样,我会在你的界面中有控件,在它引用它之后将参数传递给 GUI 对象,然后 GUI 可以是它自己的黑盒子来与逻辑。用户不必担心逻辑。只有 GUI 关心逻辑。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-07
      • 2011-09-24
      • 1970-01-01
      • 2012-08-13
      • 1970-01-01
      相关资源
      最近更新 更多