【问题标题】:Use derived type for member variable in derived class对派生类中的成员变量使用派生类型
【发布时间】:2017-06-16 13:35:52
【问题描述】:

我目前有一个 A 类,它有一些我想在 B 中使用的功能。A 有一个 Button 类型的成员变量,而在 B 中我想使用 IconButton 类型(派生自 Button)。

类是这样的:

public class A {

    protected Button x = new Button();

    A() {
        x.xx();
    }

    public void doFirstThing() {
        x.xx();
    }

    Button getButton() {
        return x;
    }
}

public class B extends A {

    B() {
        x = new IconButton();

        getButton().yy();
    }

    public void doSecondThing() {
        getButton().zz();
    }

    IconButton getButton() {
        return (IconButton) x;
    }
}

public class Button {

    public void xx() {

    }   
}

public class IconButton extends Button {

    public void yy() {

    }

    public void zz() {

    }
}

如您所见,方法yy()zz() 仅在IconButton 中可用,因此我需要通过提供方法getButton 在B 中执行“解决方法”。

是的,通常我们在 A 中也有一个 getButton() 方法,所以这并不是对 B 的牵强附会。

但我想知道,这真的是正确的做法还是我做错了?

我也想过创建一个抽象类,例如A0,它将包含来自 A 的当前代码。然后我会做:

class A extends A0<Button> {
 // mostly empty, code taken from A0
}

class B extends A0<IconButton> {
// custom things here
}

或者还有其他方法可以更好地解决这个问题吗?

这是我的 GWT 应用程序中的一个组件。所以,如果我像@king_nak 所说的那样声明,那么我的组件中将有两个按钮。或者,如果我可以接受另一种解决方法,我可以删除 B 中的原始按钮,例如x.removeFromParent() 或类似的东西,但它会成为一个全新的不良做法。

【问题讨论】:

  • abstract class A&lt;T extends Button&gt; 的字段为 T xclass B extends A&lt;IconButton&gt; 怎么样?
  • 我不确定您是否打算在某个时候构造A 类的实例。如果是这样,那么没关系,除了不需要IconButton getButton()。如果您不会创建 A 的实例,则将其设为抽象是
  • IconButton 是 B 的一个实现细节。为什么要将 IconeButton 和 Button 暴露给这些类的客户端?

标签: java oop gwt object-oriented-analysis


【解决方案1】:

您的解决方法是个坏主意。你失去了很多类型安全。你可以例如将 B 的构造函数更改为 x = new Button(); 并忘记演员表。您的程序仍然可以编译,但在运行时会失败。

我认为你在使用通用通用超类的道路上是正确的。您可以使用成员 T xA0 定义为 A0&lt;T extends Button&gt;。可能有更好的解决方案,但如果没有大局就很难判断。

【讨论】:

  • 我更新了我的问题,提供了关于我的用例的更多细节。它是一个 GWT 应用程序(A 和 B 是真正的组件),并且 yy() 和 zz() 也需要在 A 和 B 内部的许多地方使用。您对这个案例还有什么建议吗?
  • @ramcrys 不,我不是 GWT 专家。要从大局中彻底回答这个问题,这对于 stackoverflow 来说会变得很宽泛。
  • 接受了答案 :) 我已经在我的代码中使用了泛型方法。将来必须观察它的表现。
【解决方案2】:

一种简单(和多态)的方法是覆盖 B 类中的 getButton 并返回 IconButton 的实例,而不是在类中完全引用,例如:

public class A {
    public Button getButton(){
        Button b = new Button();
        b.xx();
        return b;
    }
}

public class B extends A {

    @Override
    public Button getButton(){
        IconButton b = new IconButton();
        b.yy();
        b.zz();
        return b;
    }
}

【讨论】:

  • 在这种情况下,如果你问我,有一些工厂设计模式会导致代码更简洁。
  • 如果getButton方法返回的实例类型依赖于覆盖它的类,那么我宁愿采用这种方式而不是实现工厂方法。
  • 如果类型(取决于类)是从Button 继承的,为什么要这样做?一个干净的方法是一个界面,但我不确定在这种情况下,因为我没有看到 OP 的缩进清晰。呵呵,在设计软件解决方案时,这种讨论很常见。
  • @DarshanMehta:我认为你错过了我的问题的重点。实际上我并不关注 getButton()。这只是我试图在 B 中使用 x 来欺骗的一个例子。根本问题是 x 可以在 A 和 B 中的很多地方调用,因此我认为我们不能只是将事物“分组”到 getButton 方法像这样:(
【解决方案3】:
  1. 提取按钮界面 公共接口按钮{ 无效初始化(); }
  2. 创建 PlainButton(您的前 Button)类: 公共类 PlainButton 实现 Button { 公共无效初始化(){ xx(); } 私人无效xx(){ } }
  3. 创建 IconButton 类: 公共类 IconButton 实现 Button { 公共无效初始化(){ yy(); Z Z(); } 私人无效yy(){ } 私人无效zz(){ } }
  4. 创建 ButtonWrapper 类(您的前 A 和/或 B 类): 公共类 ButtonWrapper { 私人最终 T 按钮; 公共ButtonWrapper(T按钮){ this.button = 按钮; this.button.init(); } 公共T getButton(){ 返回按钮; } }
  5. 用途:

    ButtonWrapper A = new ButtonWrapper(new PlainButton()); ButtonWrapper B = new ButtonWrapper(new IconButton());

【讨论】:

    【解决方案4】:

    如果您唯一关心的是B 应该有一个IconButton 而不是它的基础Button,为什么不在B 中为它创建一个单独的成员,让A 使用它?

    class A {
        protected Button b;
        public A() {
          // By default, I use a Button
          b = new Button();
        }
    
        // Subclasses can override which button I use
        protected A(Button b) {
           this.b = b;
        }
    
    }
    
    class B extends A {
        private IconButton ib;
        public B() {
          super(new IconButton());
          ib = (IconButton) super.b;
        }
    
        // B can access it's IconButton through ib
        // Users of A will see a Button
    }
    

    不需要抽象基类、泛型、包装器。让 B 拥有它的 IconButton,并有 2 个对它的引用(B.ib 并继承 A.b

    【讨论】:

    • 啊啊啊啊...我忘了说这是我的 GWT 应用程序中的一种组件。所以,如果我像你说的那样声明,那么我的组件中将有两个按钮......(或者如果我可以接受另一种解决方法,我可以删除 B 中的原始按钮。但它只是变成了一个全新的水平不良做法)
    • 实例化 B 时只有 1 个按钮。当子类提供一个按钮时,A 从不创建按钮。或者您正在处理生成的代码并且不能使用这种方法?
    • 哇.. 看来这就是我需要的。但是当我们有两件事(b和ib)在概念上应该只是一个时,它看起来很奇怪/在这里引入了一种不好的做法吗?我的意思是,您的方法可能是最实用的方法,但它真的是一种简洁的设计吗?
    • 这是错误的。你不能将ib 传递给super 构造函数。
    • 好吧,派生属性有 UML 表示法...我以此为理由,但不能说它是否真的很干净。固定版本更清楚地表明ib 只是b 的类型转换别名。所以我会说没关系。取决于您的编码指南和标准,但是...
    【解决方案5】:

    如果您不想使用泛型: 这种方法与best practices 背道而驰,但我发现它完全没问题,只要你不在B 的构造函数中使用按钮做任何事情:

    public static class A {
    
        protected Button x = null;
    
        public A() {
            x = createButton();
        }
    
        public Button getButton() {
            return x;
        }
    
        protected Button createButton(){
            Button result = new Button();
            result.xx();
            return result;
        }
    }
    
    public static class B extends A{
    
        public IconButton getButton() {
            return (IconButton)x;
        }
    
        protected Button createButton(){
            IconButton iconButton = new IconButton();
            iconButton.yy();
            iconButton.zz();
            return iconButton;
        }
    }
    

    现在,如果您想避免演员表,您可以使用这种我自己很可能不会使用的疯狂方法。 警告使用风险自负:

    public static class A {
    
            protected Button x = null;
    
            public A() {
                x = createButton();
            }
    
            public Button getButton() {
                return x;
            }
    
            protected Button createButton(){
                Button result = new Button();
                result.xx();
                return result;
            }
        }
    
        public static class B extends A{
    
            //leave uninitialized so that when B gets constructed iconButton wont be reinitialized because it has been initialized by A's constructor call
            private IconButton iconButton; 
    
            public IconButton getButton() {
                return iconButton;
            }
    
            protected Button createButton(){
                iconButton = new IconButton();
                iconButton.yy();
                iconButton.zz();
                return iconButton;
            }
        }
    

    更新:

    如前所述,首选方法是在此处避免继承,并在需要时使用组合。但是由于示例代码没有太多细节以便能够显示组合方法,因此我添加了另一种仍然使用继承的方法,避免了强制转换和构造初始化。 我认为在使用 Swing 或 GWT 等非并发框架时最好使用惰性初始化,而不是构造初始化:

    public static class A {
    
            private Button x = null;
    
            protected Button getButton() {
                if (x == null) x = createButton();
                return x;
            }
    
            private Button createButton(){
                Button result = new Button();
                result.xx();
                return result;
            }
        }
    
        public static class B extends A{
    
            private IconButton iconButton; 
    
            public Button getButton() {
                if (iconButton == null) iconButton = createButton();
                return iconButton;
            }
    
            public IconButton getIconButton(){
                getButton();
                return iconButton;
            }
    
            private IconButton createButton(){
                IconButton iconButton = new IconButton();
                iconButton.yy();
                iconButton.zz();
                return iconButton;
            }
        }
    

    【讨论】:

    • 实际上我不只是在 B 的构造函数中使用 x。换句话说,B 中可能有很多其他方法我想调用一些特殊的方法(仅来自 IconButton )。所以我们只需要求助于我原来的解决方法:在所有这些地方调用 getButton() :(
    • 您需要将所有iconButton初始化代码保留在createButton()中。
    • 是的,我可以在 createButton() 中初始化代码。但是,如果我有一个需要调用 x.yy() 的方法 B.handlerForEventXXX() 怎么办。这是不可能的 => 我必须调用 getButton().yy()...(我说的是你的第一种方法)。请查看我在问题中更新的代码。
    • 是的,您需要对x 进行强制转换或调用getButton 才能访问yy()
    • 是的,这就是我问题的重点。我想知道这是否是一种非常糟糕的做法(如下面的@andre_stannek 回答中所述),所以我最好使用泛型。
    猜你喜欢
    • 2017-12-02
    • 2021-07-25
    • 1970-01-01
    • 1970-01-01
    • 2021-01-15
    • 2016-05-21
    • 2018-11-30
    • 1970-01-01
    • 2015-02-04
    相关资源
    最近更新 更多