类的实例和表示实例的类之间的区别?

Kor*_*gay 19 oop class

我使用Java作为示例,但这更像是一般的OOP设计相关问题.

让我们以IOExceptionJava中的s为例.为什么有一个类FileNotFoundException例如?难道不应该是一个实例IOException,其中的原因FileNotFound?我想说的FileNotFoundException是一个例子IOException.这到底在哪里?FileNotFoundButOnlyCheckedOnceException,FileNotFoundNoMatterHowHardITriedException..?

我也看到过我所在的项目中的代码,其中包括类FirstLineReaderLastLineReader存在的类.对我来说,这些类实际上代表了实例,但我在很多地方看到了这样的设计.以Spring Framework源代码为例,它带有数百个这样的类,每次看到一个我都看到一个实例而不是蓝图.这些课程不是蓝图吗?

我想问的是,如何在这两个非常简单的选项之间做出决定:

选项1:

enum DogBreed {
    Bulldog, Poodle;
}

class Dog {
    DogBreed dogBreed;
    public Dog(DogBreed dogBreed) {
        this.dogBreed = dogBreed;
    }
}
Run Code Online (Sandbox Code Playgroud)

选项2:

class Dog {}

class Bulldog extends Dog {
}

class Poodle extends Dog {
}
Run Code Online (Sandbox Code Playgroud)

第一个选项为调用者提供了配置它正在创建的实例的要求.在第二个选项中,类已经表示了实例本身(正如我所看到的,这可能是完全错误的......).

如果你同意这些类代表实例而不是蓝图,你会说创建表示实例的类是一个好习惯,或者我正在看这个是完全错误的,我的语句"表示实例的类"只是加载废话?

Gho*_*ani 18

编辑

首先:我们知道继承定义,我们可以在SO和互联网上找到很多例子.但是,我认为我们应该深入而且更加科学.

注0:
关于继承实例术语的说明.
首先让我为开发生命周期命名开发范围,当我们对系统和运行时范围进行建模和编程,有时我们的系统正在运行.

我们有类和建模,并在开发范围内开发它们.和运行时范围内的对象.开发范围中没有对象.

在面向对象中,实例的定义是:从类创建对象.

另一方面,当我们谈论类和对象时,我们应该澄清关于开发范围运行时范围的观点.

因此,通过这个介绍,我想澄清继承:
继承是类,非对象之间的关系.
继承可以存在于开发范围中,而不是存在于运行时范围中.运行时范围中没有继承.

在运行我们的项目之后,父项和子项之间没有任何关系(如果子类和父类之间只有继承).所以,问题是:什么是super.invokeMethod1()super.attribute1,他们不是孩子和父母之间的关系.父节点的所有属性和方法都传输给子节点,这只是访问从父节点传输的部分的符号.

此外,开发范围中没有任何对象.因此,开发范围中没有任何实例.它只是Is-AHas-A的关系.

因此,当我们说:

我想说的FileNotFoundException是一个实例IOException

我们应该澄清我们的范围(开发和运行时).
例如,If FileNotFoundException是一个实例IOException,那么FileNotFoundException运行时特定异常(Object)与之间的关系是什么FileNotFoundException.它是实例的实例吗?

注1:
为什么我们使用继承?继承的目标是扩展父类功能(基于相同类型).

  • 可以通过添加新属性或新方法来实现此扩展.
  • 或覆盖现有方法.
  • 另外,通过扩展父类,我们也可以达到可重用性.
  • 我们不能限制父类功能(Liskov Principle)
  • 我们应该能够将孩子替换为系统中的父母(Liskov Principle)
  • 等等.

注意2:
继承层次结构
的宽度和深度继承的宽度和深度可以与许多因素相关:

  • 项目:项目的复杂性(类型复杂性)及其架构和设计.项目规模,班级数量等
  • 团队:团队在控制项目复杂性方面的专业知识.
  • 等等.

但是,我们有一些启发式方法.(面向对象的设计启发式,Arthur J. Riel)

从理论上讲,继承层次结构应该越深越深越好.

在实践中,继承层次结构应该不比一般人可以保留在他或她的短期记忆中更深.这个深度的流行价值是六.

请注意,它们是启发式的,基于短期记忆编号(7).也许团队的专业知识会影响这个数字.但在许多层次结构中使用了组织结构图.

注3:
当我们使用错误的继承?
基于 :

  • 注1:继承的目标(扩展父类功能)
  • 注2:继承的宽度和深度

在这种情况下,我们使用错误的继承:

  1. 我们在继承层次结构中有一些类,没有扩展父类功能.扩展应该是合理的,应该足以创建一个新类.从观察者的角度来看,这是合理的手段.观察者可以是项目建筑师或设计师(或其他建筑师和设计师).

  2. 我们在继承层次结构中有很多类.它称为过度专业化.一些原因可能导致:

    • 也许我们没有考虑注1(扩展父功能)
    • 也许我们的模块化(包装)不正确.我们将许多系统用例放在一个包中,我们应该进行设计重构.
  3. 他们是其他原因,但这个答案并不完全相关.

注4:
我们该怎么办?当我们使用错误的继承?

解决方案1:我们应该执行Design Refactoring来检查类的值,以便扩展父功能.在这种重构中,可能删除了许多类系统.

解决方案2:我们应该执行设计重构以进行模块化.在这个重构中,我们的包的一些类可能会传输到其他包.

解决方案3:使用Composition over Inheritance.
我们可以使用这种技术有很多原因.动态层次结构是我们更喜欢使用Composition而不是Inheritance的常见原因之一.
看到Tim Boudreau(太阳报)在这里注意到:

对象层次结构不可扩展

解决方案4:在子类上使用实例

这个问题是关于这种技术的.让我将它命名为Subclasses上的实例.

当我们可以使用它时:

  1. (技巧1):当我们没有完全扩展父类功能时,请考虑注1.或者扩展名不够合理.

  2. (技巧2 :)考虑注2,如果我们有很多子类(半类或相同的类),它们稍微扩展了父类,我们就可以在没有继承的情况下控制这个扩展.请注意,这并不容易.我们应该证明它并没有违反开放式原则等其他面向对象原则.

我们应该做什么?
Martin Fowler建议(第1册第232页和第2册第251页):

用字段替换子类,将方法更改为超类字段并消除子类.

我们可以使用其他技术,如enum提到的问题.