子类OOP的局限性

时间:2013-06-13 06:27:47

标签: java oop

在java中,围绕一个减少超类功能的子类设计的好方法是什么?

例如,考虑具有“See”函数的类“Man”和不应该具有该函数的子类“BlindMan”,但应该具有“Man”具有的所有其他内容。

我能想出的唯一解决方案是拥有一个抽象类“Man”,以及两个子类“SeeingMan”和“BlindMan”,SeeinMan添加了一个“See”函数。

然而,这个解决方案的问题是如果我现在想要添加一个“DeafMan”类 - 它会扩展什么? SeeingMan?如果那个男人既聋又瞎眼怎么办?

13 个答案:

答案 0 :(得分:14)

我认为你应该在这种情况下使用Composition而不是继承,或者有各种构成人类的子类。

虽然我理解你的逻辑,但BaseClass是一个契约,它保证该类型的所有类都应该遵循这种行为,拥有一个删除父方法的子类是一个很大的NO NO ..

虽然你可以抛出各种例外,但我根本不会走这条道路。 以这种方式思考,说我是一个只需要访问Human对象的开发人员,我期待某种行为,突然之间我调用了一个接口方法并获得了一个例外,因为我调用了它?您不应该知道派生类的实现以及何时可以或不能调用它们。

以下是一些解决方案:

让人类成为BasicHumanFunctions,VisionSystem等的组合。那么盲人只有少数几个。

class Human {
  private BasicHumanFunctions _basicFunctions; //contains breathe funcitonality.
  private VisionSystem _vision; //contains see
}

class BlindMan {
   private BasicHumanFunctions _basicFunctions;
}

让人类基类只包含所有人类想呼吸的相同行为等,然后创建一个HealthyHuman和一个BlindHuman等,每个人都创建自己的方法。 然后,如果需要,您可以进一步使用HealthHuman和子类。

class Human {
   void breathe() {};
   // other Human basic functions;
} 

class HealthyHuman extends Human {
   void see() {};
   //other healthy human functions
}

class BlindHuman extends Human {
  void useCane() {};
}

对于第二种情况,您仍然可以使用合成来分享行为:

class BlindHuman extends Human {
   private VoiceSubsystem _voice = new VoiceSybsystem();
   void speaker() {  _voice.speaker();}
}

答案 1 :(得分:8)

最重要的原则是利斯科夫的替代原则。

获得并应用后,您可以根据需要构建模型。

例如,你可以说一个盲人仍然看到,只有他才会得到黑色(或灰色)图片。还有一个问题是,如果模糊或局部的情况,您仍然希望看到合法盲人。

你也可以说对象可以做的动作是看不到的。我们要做的就是看。看是观看的(成功)结果。即使有工作的眼睛,你可能会看到没有看到任何东西!盲人仍然可以通过聆听(从而听到并建立一个心理“图片”)来“看到”没有眼睛或没有功能的眼睛的东西。

所以,忘记“看”和“听”。使用手表和听。并期待一切。 (广泛的前提条件)。

答案 2 :(得分:4)

转到接口

定义CommonMan的接口,它具有公共属性并使用单独的接口用于具有特定方法的单独子类。然后,您可以获得两种接口方法。

示例:

public class SpecificMan extends CommonMan implements specifiManInter

{

//methods of CommonMan

//Methods of SpecificMan 

}

CommonMan类:

public class CommonMan implements CommonManInter {

 //methods of CommonMan

}

答案 3 :(得分:2)

Dory有利于构成而不是继承。你的问题/问题与飞鱼问题几乎相同。在这个例子中,我们应该如何处理可以飞游泳的生物?

enter image description here

将FlyingFish放入继承层次结构中会很尴尬。而不是尝试实现这一点,使用组合的力量:

enter image description here

现在,每个具体类都可以指定它们的行为。根据信息专家原则,这是适当的地方:Information Expert will lead to placing the responsibility on the class with the most information required to fulfill it

注意:很难从上面的屏幕截图中看到好处,所以这里又是:

<强>优势
- 赞成对象构成优于继承
- 强制开发人员在编写代码时有意识地决定行为(而不是在基类中创建开发人员必须记住要覆盖的默认实现)。
- 通过抽象变化的行为,它现在可供其他客户使用 - 更松散耦合意味着更容易遵守开放/封闭原则,其中代码应该是可以扩展的,但是关闭以进行修改。

答案 4 :(得分:1)

在课堂上添加hasEyes,默认情况下为真,而在BlindMan中默认为假(或者让盲人实例化的人眼睛立刻被抢劫,例如{ {1}})。然后在Man的BlindManFactory代码中,如果他没有眼睛就会自动失败。

答案 5 :(得分:1)

你可以使用布尔

例如:isSeeing()

并且在您的函数中,您可以在if else语句中使用它

答案 6 :(得分:1)

  

在java中,围绕一个减少超类功能的子类设计的好方法是什么?

这基本上问“如何继承以减少功能?”。在Java中我可以想到的一个例子是Collections#unmodifyableList方法,它说:

  

返回指定列表的不可修改视图。此方法允许模块为用户提供对内部列表的“只读”访问。对返回列表的查询操作“读取”到指定列表,并尝试修改返回的列表,无论是直接还是通过其迭代器,都会导致 UnsupportedOperationException

这就是我想删除某个功能时的目的。但是,请确保其使用清晰,例如BlindMan的Javadoc状态see()将导致异常。

但是,如果你想“减少”多个功能,可能是独立的,并且随着时间改变它们,我宁愿使用变量而不是继承。最后,你没有扩展超类,为什么滥用继承呢?你拥有的是具有不同属性的人。

需要记录方法的行为。我可以为您的用例考虑以下类:

public class Human {
    private boolean seeing;

    public void setSeeing(boolean seeing) { ...}
    public boolean isSeeing() { ... }

    /**
     *  Returns what the human sees at the moment, or null
     *  if he/she can't see (seeing is false).
     */
    public String see() {
        if(seeing){
            return "I can see a tree!";
        }else{
            return null;
        }
    }

    // same goes for hearing
}

答案 7 :(得分:1)

让人类以自己的方式做事,给他任务,而不是命令。

看到是某些活动的一部分所以让他独立使用视觉(或不视觉)。

告诉他去某个地方旅行。健康的人会使用视力,盲人会使用其他感官,效率会降低(除非它完全黑暗)。

如果对于特定的任务需要视力(例如,警卫塔上的警卫),你需要问他们的视力有多好(没有盲目的人有着非常好的视力)。

有时候你想问他们看到了什么(并不关心他们听到了什么等等)然后只是询问并期望盲人(以及其他视力受损者)看不到任何东西。

如果你想让人类成为被动对象和其他一些处理它们的对象,那么一个尚未提及的方法是: 看看游戏编程中使用的实体组件编程。它基本上是以通用方式使用组合。实体仅保持身份,组件保存关于实体的某些方面的数据(例如视线可以是例如屈光度数或一些更有用的统计数据)和系统处理具有给定系统所需的组件组合的实体集。 视线系统可能需要视觉组件并创建组件seenEntities包含当前实体现在看到的实体。 其他系统可以合并所有感官的结果,并使用它来执行有用的事情。

答案 8 :(得分:1)

  

例如,考虑班级&#34; Man&#34;,它具有&#34;参见&#34;和   子类&#34; BlindMan&#34;不应该有这个功能

你没有完全正确。盲人有这个功能&#34;看&#34;什么都不做(因为传感器和适当的大脑部分之间没有联系,或者传感器没有运作(返回空的像素列表或0大小的图像)。

一个很好的类比:Writerflush()函数,但StringWriter的函数是什么?

/**
 * Flush the stream.
 */
public void flush() { 
}

嗯,没什么。没有什么可以冲洗的。好的但为什么close()会抛出IOException

public void close() throws IOException {
}

它并不完美,但该代码适用于所有类型的作者,即使flush不相关的作家也是如此。

但是等等,即使是看见人函数see()也可能返回null或空列表。例如,当它太暗时。只需检查null / empty值,代码就可以正常使用该设计。

答案 9 :(得分:0)

如果您不希望调用该函数,可以抛出异常。

e.G。在您的方案中:InablityException

像这样:

public interface Man {

    public Object see() throws InabilityException;
    public Object hear() throws InabilityException;

}

public class BlindMan implements Man {
    public Object see() throws InabilityException {
       throw new InabilityException("blind people don't see");
    }

    public Object hear() {
       return "what I hear";
    }
}

对聋人而言反之亦然。

如果Man是class而不是interface,则同样有效。

如果throw返回false,您可以(并且应该)将此与@Pataschu答案https://stackoverflow.com/a/17080626/881272Exception hasWorkingEyes()结合起来。

答案 10 :(得分:0)

接口

我将通过接口定义两种行为(BlindMan和SeeingMan),仅暴露定义类型的调用(BlindMan和SeeingMan)。您不应该真正关心底层的实际实例类型。

抽象父类

现在,您可以将这些接口与具有所有默认行为的“父”抽象类相结合(盲目和视觉;如果您也选择)

合并两个

public interface BlindMan {
         public void useBlindStick();
    }

   public interface SeeingMan {
        public void see();
   }


  public abstract class Man {
       public void useBlindStick() {
           //work
       }

       public void see() {
           //work
       }
  }


  public class SomeBlindMan extends Man implements BlindMan {
        //bla bla
  }


 public class SeeingMan extends Man implements SeeingMan {
       //bla bla
 }

希望有所帮助

答案 11 :(得分:0)

这样的事情怎么样:

 public class Man {

    Ears ears;

    Eyes eyes;

    }

    public class Eyes {

    public boolean canSee()

    }

    public class Ears {

    public boolean canListen()
    }

然后逻辑就像......

if(man.getEyes().canSee()){ //not blind

}
else
{
//blind logic
}

if(man.getEars().canListen())
{
//Not Deaf
}

答案 12 :(得分:0)

要实施功能,请使用界面

要扩展或重新定义功能,请使用扩展基类的子类。

如果要在运行时动态添加行为,请实现Decorator模式。

相关的SE帖子:

Implements vs extends: When to use? What's the difference?

When to Use the Decorator Pattern?