Java覆盖方法删除异常

时间:2012-10-07 14:31:00

标签: java oop design-patterns inheritance interface

总结

对象A具有抛出异常的方法{m1,m2,...};在验证之后,其中一些方法将再次被称为而不是throw。在OO中建模验证阶段的进展,当运行检查并返回正结果时,对象被“提升”到对其方法可靠性的更高置信度,并且不再对客户端强制进行异常检查



完整版

你能否建设性地批评这个设计选择:


  • 接口描述了对象“类”的功能。为了“成为”一个这样的对象,一个实例支持接口协议就足够了,它只能建立“尝试”一定数量的东西的能力(实现抛出异常的方法)

  • 对于上述情况,一种简单的存在形式是可以尝试所有操作的阶段,但是在很多情况下预计会失败(未受过教育的野蛮人是一个人,因为他可能会尝试他的所有任务都是人类,但是没有人会对他们中任何一个人的成功下注很多)

  • 基本接口的子类型保持相同的功能,但表示完成高级验证,以确保其某些操作始终成功。这个“促销”和演员跟随检查内部状态和合同先决条件“升级”对象的顺序


你能指点我相应的模式,或者确实是反模式(如果你想阻止我采用这个想法),它通过验证阶段获取对象的相同概念关于基本操作的可靠性随渐进式检查而增加?

我试图通过接口层次结构对此进行建模,其中操作都在基本接口中,并且存在相关的已检查异常,但存在子类型(子接口),其中异常从方法签名中消失。

我在发布此处之前考虑过装饰器模式,但它在许多层面上失败,无法模拟将对象标记为“已验证到某一点”的原则。我还考虑了“基于继承的组合”,其中关于验证阶段的元数据(一个枚举?)被组合到对象中并且switch被编辑。

主要目标是:


  • 如果对某个对象的给定“标本”一无所知,请客户检查以查看异常

  • 使客户免于在将对象发送到验证层并在“已检查并正常工作”时返回时必须检查异常的责任。使用更高阶的界面意味着:“这是快速且安全的使用”

  • 允许客户选择尝试立即使用该对象,但处理可能发生的奇怪案例,或转发它到验证,在试图调用方法之前,慢速委托。当然,委托会返回对象的高阶界面强制转换信号可靠性


即使设计困境对我来说至关重要,也会产生一种侮辱性的表现:


interface CivilisedMan extends Man {
  @Override
  void act();
}
interface Man {
  void act() throws UnreasonableBehaviourException;
}
class UnreasonableBehaviourException extends Exception {
  public UnreasonableBehaviourException(String embarrassingCircumstance) {
    super(embarrassingCircumstance);
  }
}
public class StackOverflow {
  public static void main(String[] args) {
    Man williamConnollyJr = new Man() {
      @Override
      public void act() throws UnreasonableBehaviourException {
        throw new UnreasonableBehaviourException("F*rt!");
      }
    };
    CivilisedMan harryPotter = new CivilisedMan() {
      @Override
      public void act() {
        System.out.println("Swish and flick.");
      }
    };
    try {
      williamConnollyJr.act();
    } catch (UnreasonableBehaviourException unreasonableBehaviourException) {
      System.out.println(unreasonableBehaviourException.getMessage());
    }
    harryPotter.act();
  }
}

我很好地废弃了这个设计,如果需要的话可以重新开始,但我需要一些备份参考来做到这一点......

注意:这种行为模式在生活中经常发生。你拿起一个新的物体,对它一无所知,对你如何扔它/旋转它/变形它没有什么期望/ ......你检查的越多,“尝试它”,你建立的保证就越多关于每个设想的动作对象的适用程度......

3 个答案:

答案 0 :(得分:1)

正如已经说过的,对非特殊事物使用例外并不是一个好主意。对我而言,看起来像是Guava的Optional或类似的类别的明显案例。我可以做更多,也许像

@lombok.RequiredArgsConstructor(access=AccessLevel.PRIVATE)
class Result {
    // the computed value if case of a success
    private final Value value;
    // the failure reason  if case of a failure
    private final Problem problem;

    public static success(Value value) {
        return new Result(checkNonNull(value), null);
    }
    public static fail(Problem problem) {
        return new Result(null, problem);
    }

    public boolean isSuccess() {
        return value!=null;
    }
    public Value value() {
        if (!isSuccess()) throw new SomeRuntimeException();
        return value;
    }
    public Value problem() {
        if (isSuccess()) throw new SomeRuntimeException();
        return problem;
    }
}

通过返回Result,你的问题没有解决,但很好地支持了一步。

  • 客户端检查没有异常,但由于忘记检查成功而导致的错误被阻止,因为您的方法不返回任何可直接使用的东西 - 它们返回Result并且客户端必须提取{ {1}}首先提醒他们先检查value

  • 这些检查既快又简单,所以我不会试图从中释放客户端。如果你真的想要,那么创建一个类层次结构并在子类中实现像isSuccess这样的方法(除了超类中的Value actDirectly()之外)。但是(正如已经说过的),这可能导致类爆炸,并且向下转换也不会产生可读代码。

  • 关于慢速验证和快速路径,这应该是恕我直言的客户端隐藏,并由您的类内部处理,这应该缓存验证结果等内容。关于客户的选择,我不确定你的意思,但也许你可以为你的方法提供和论证Result act()(或者更像是附加类或其他类似的东西)。


我没有看到推广的价值,但如果我这样做,我会做类似

的事情
unchecked

答案 1 :(得分:0)

老实说,我不喜欢你的设计,原因如下:

  • 您在某些操作意外的地方使用例外。注意细微差别。应该使用例外来表示异常,不稳定的行为。非法状态,错误引用,缺少文件。如果您希望定期频繁发生异常,请使用不同的构造。

  • 您没有显示不安全验证的对象之间的区别。您如何判断,实现CivilisedMan某个对象的实例是否已经过验证?你提到有关“升级”到经过验证的级别和一些特殊的子界面。这意味着您必须将接口数量增加一倍,从而引入大量重复。

  • 使用已检查的例外。避免他们。实际上在你的情况下,他们是好的,因为他们是预期的 - 但你不应该期望例外(他们是...意外)


那么我建议采用什么样的设计呢?就这样做吧。界面不应该表示“尝试”的能力 - 它应该表明能力。如果你的对象实现了CivilisedMan,那么它就是一个文明的人。如果没有,它就无法执行act()而且不是文明的。周期。

Object williamConnollyJr = //...
Object harryPotter = //...
if(williamConnollyJr instanceof CivilisedMan) {
    ((CivilisedMan)williamConnollyJr).act();
} else {
  System.out.println(unreasonableBehaviourException.getMessage());
}
if(harryPotter instanceof CivilisedMan) {
    ((CivilisedMan)harryPotter).act();
}

优点:

  • 更清洁,更易读,尽管有低价倾向
  • 验证/升级是自动的 - 一旦您检查并从Object下降到CivilisedMan,该对象就会得到验证。
  • 如果需要,您可以使用装饰器模式回退到以前的设计
  • 您可以使用java.lang.reflect.Proxy动态编写不同的功能。

缺点

  • instanceofObject正在使用
  • 动态性较差,对象是否为CivilisedMan必须在编译时决定,而不是在运行时决定

答案 2 :(得分:0)

恕我直言,走自己的路会很痛苦。

为了避免假设Man有3个方法,每个方法抛出3个异常......对于每种方法,你需要8(2 ^ 4)个变种(不投掷,抛出一个,投掷两个,......)。所以你需要24(3 * 8)个专用接口...... auch。

在没有太多开销的情况下,Java中不可能进行升级。

我会尝试使用策略,如果不足以在其上添加一些责任链的变体。

所以它会这样:

class UnreasonableBehaviourException extends RuntimeException {}

interface Man {
  boolean canAct();
  void act();
}

现在,如果有人想使用某种方法,他就会有一种干净的方法。如果他不在乎这是他的问题。

Man man = new Man() {
  ...
}

if(man.canAct()) man.act();

如果界面很简单(方法不多),我会在这里停下来。如果界面很复杂:

interface SomethingToDo {
  public void act(Man man);
}

class CanAct {
  private final Man man;
  private SomethingToDo next;

  public CanAct(SomethingToDo next) {
    this.next = next;
  }

  public void act(Man man) {
    if(man.canAct()) {
      man.act();
    }
    if(next == null) return;
    next.act();
  }
}

class MustAct {
  private SomethingToDo next;


  public CanAct(SomethingToDo next) {
    this.next = next;
  }

  public void act(Man man) {
    if(man.canAct()) {
      man.act();
      if(next == null) return;
      next.act();
    }
    else {
      ... some error handling ...
    }
  }
}

扩展此示例,您可以创建要执行的操作:

SomethingToDo toBeDone = new MustDo1(new MustDo2(new CanDo3(new MustDo2(null))));

toBeDone.act(new SavageMan()) <- will fail due to something
toBeDone.act(new Nobleman())  <- will succeed

<强>赞成

  • 通过测试能力完成上传
  • 您可以定义复杂的操作(如果他可以使用,而不能执行其他操作)
  • 易于阅读人应该具备的能力
  • SomethingToDo.act的简洁实现
  • 促销是自动的,您可以或不可以完成操作。
  • 你不需要考虑所有可能性。您的用户将为您执行此操作(他们将实现SomethingToDo接口)
  • 无论您的用户是否被例外挂起,都是您的决定(SomethingToDo的实施方式)

<强>缺点

  • 错误处理可能很复杂,尤其是在需要回滚时。
  • 可以生成大量实现SomethingToDo的类
  • Man界面中每种方法的附加方法