多态性vs覆盖vs重载

在Java中,当有人问:

什么是多态性?

重载压倒一切的是可以接受的答案吗?

我认为事情远不止如此。

如果你有一个抽象基类,它定义了一个没有实现的方法,并且你在子类中定义了这个方法,这仍然是覆盖的吗?

我认为重载肯定不是正确答案。

301490 次浏览

术语重载指的是具有相同名称的东西的多个版本,通常是具有不同参数列表的方法

public int DoSomething(int objectId) { ... }
public int DoSomething(string objectName) { ... }

这些函数可能做同样的事情但你可以选择用ID或名称来调用它。与继承、抽象类等无关。

覆盖通常指的是多态性,就像你在问题中描述的那样

你说得对,超载不是解决问题的办法。

两者都不是最重要的。覆盖是获得多态性的方法。多态性是对象根据其类型改变行为的能力。当显示多态的对象的调用者不知道该对象是什么具体类型时,最能说明这一点。

重载是指定义两个名称相同但参数不同的方法

重写是通过子类中具有相同名称的函数更改基类的行为。

所以多态性与覆盖有关,但不是真正的重载。

然而,如果有人对“什么是多态性?”这个问题给出了一个简单的“重写”的答案。我要求进一步解释。

多态性是一个对象以多种形式出现的能力。这涉及到使用继承和虚函数来构建可交换的对象族。基类包含虚函数的原型,这些原型可能未实现,也可能具有应用程序指定的默认实现,而各种派生类都以不同的方式实现它们,以影响不同的行为。

经典的例子,狗和猫是动物,动物有制造声音的方法。我可以迭代调用makeNoise的动物数组,并期望它们在各自的实现。

调用代码不需要知道它们是什么特定的动物。

这就是我所说的多态性。

下面是伪c# /Java中的多态性示例:

class Animal
{
abstract string MakeNoise ();
}


class Cat : Animal {
string MakeNoise () {
return "Meow";
}
}


class Dog : Animal {
string MakeNoise () {
return "Bark";
}
}


Main () {
Animal animal = Zoo.GetAnimal ();
Console.WriteLine (animal.MakeNoise ());
}

Main函数不知道动物的类型,并且依赖于MakeNoise()方法的特定实现的行为。

编辑:看来布莱恩先我一步了。有趣的是我们用了同样的例子。但是上面的代码应该有助于阐明概念。

重写和重载都用于实现多态性。

你可以在一个类中有一个方法 即覆盖 in one或 更多的子类。这个方法可以 不同的东西取决于哪个 类用于实例化一个对象
    abstract class Beverage {
boolean isAcceptableTemperature();
}


class Coffee extends Beverage {
boolean isAcceptableTemperature() {
return temperature > 70;
}
}


class Wine extends Beverage {
boolean isAcceptableTemperature() {
return temperature < 10;
}
}

你也可以有一个方法 重载带有两个或多个参数集。这个方法可以 不同的东西基于

.传入参数的类型
    class Server {
public void pour (Coffee liquid) {
new Cup().fillToTopWith(liquid);
}


public void pour (Wine liquid) {
new WineGlass().fillHalfwayWith(liquid);
}


public void pour (Lemonade liquid, boolean ice) {
Glass glass = new Glass();
if (ice) {
glass.fillToTopWith(new Ice());
}
glass.fillToTopWith(liquid);
}
}

没有:

重载是指使用相同的函数名,但接受不同的参数。

重写是指子类用自己的方法替换父类的方法(这本身不构成多态性)。

多态性是后期绑定,例如,基类(父类)方法被调用,但直到运行时应用程序才知道实际对象是什么——它可能是一个方法不同的子类。这是因为任何子类都可以在定义基类的地方使用。

在Java中,你可以在集合库中看到很多多态性:

int countStuff(List stuff) {
return stuff.size();
}

List是基类,编译器不知道你计数的是链表、向量、数组还是自定义列表实现,只要它像List一样:

List myStuff = new MyTotallyAwesomeList();
int result = countStuff(myStuff);

如果你超载了,你会:

int countStuff(LinkedList stuff) {...}
int countStuff(ArrayList stuff) {...}
int countStuff(MyTotallyAwesomeList stuff) {...}
etc...

编译器会选择countStuff()的正确版本来匹配参数。

多态性涉及到一种语言通过使用单一接口统一处理不同对象的能力;因此,它与覆盖有关,因此接口(或基类)是多态的,实现者是覆盖的对象(同一奖章的两个面)

无论如何,这两个术语之间的差异可以用其他语言更好地解释,比如c++: c++中的多态对象如果基函数是虚的,则行为与java对应的对象相同,但如果方法不是虚的,则代码跳转将被解析静态,并且在运行时不检查true类型,因此,多态包括对象根据用于访问它的接口而表现不同的能力;让我在伪代码中做一个例子:

class animal {
public void makeRumor(){
print("thump");
}
}
class dog extends animal {
public void makeRumor(){
print("woff");
}
}


animal a = new dog();
dog b = new dog();


a.makeRumor() -> prints thump
b.makeRumor() -> prints woff

(假设makeRumor不是虚拟的)

Java并没有真正提供这种级别的多态性(也称为对象切片)。

animal a = new dog(); Dog b = new Dog ();

a.makeRumor() -> prints thump
b.makeRumor() -> prints woff

在这两种情况下,它只会打印woff.. 因为a和b指向类dog

多态性是一个类实例的能力,它的行为就像它是它的继承树中另一个类的实例一样,通常是它的祖先类之一。例如,在Java中,所有的类都继承自Object。因此,您可以创建Object类型的变量,并将任何类的实例分配给它。

覆盖是一种函数类型,它发生在从另一个类继承而来的类中。重写函数“替换”从基类继承的函数,但这样做的方式是,即使在其类的实例通过多态性假装为不同类型时也会调用它。参考前面的示例,您可以定义自己的类并重写toString()函数。因为这个函数是从Object继承的,所以如果将该类的实例复制到Object类型变量中,它仍然可用。通常情况下,如果你在你的类中调用toString(),而它假装是一个对象,那么真正触发toString的版本是Object本身定义的版本。但是,由于函数是重写,即使类实例的真实类型隐藏在多态性后面,也会使用类中的toString()定义。

重载是定义具有相同名称但具有不同参数的多个方法的动作。它与覆盖或多态性无关。

具体地说重载或重写并没有给出全貌。多态性就是对象根据其类型专门化其行为的能力。

我不同意这里的一些答案,因为重载是一种多态形式(参数多态),在这种情况下,具有相同名称的方法可以具有不同的行为,给出不同的参数类型。一个很好的例子是操作符重载。您可以定义“+”来接受不同类型的参数——例如字符串或int型——并且基于这些类型,“+”将具有不同的行为。

多态性还包括继承和重写方法,尽管它们在基类型中可以是抽象的或虚拟的。在基于继承的多态性方面,Java只支持单个类继承,将其多态行为限制为单个基类型链。Java支持多个接口的实现,这是另一种形式的多态行为。

表达多态性最清晰的方法是通过抽象基类(或接口)

public abstract class Human{
...
public abstract void goPee();
}

这个类是抽象的,因为goPee()方法对人类是不可定义的。它只对子类Male和Female可定义。此外,人是一个抽象的概念。你不能创造一个既不是男性也不是女性的人。一定是两者之一。

因此,我们通过使用抽象类来延迟实现。

public class Male extends Human{
...
@Override
public void goPee(){
System.out.println("Stand Up");
}
}

而且

public class Female extends Human{
...
@Override
public void goPee(){
System.out.println("Sit Down");
}
}

现在我们可以让一屋子的人去尿尿了。

public static void main(String[] args){
ArrayList<Human> group = new ArrayList<Human>();
group.add(new Male());
group.add(new Female());
// ... add more...


// tell the class to take a pee break
for (Human person : group) person.goPee();
}

运行该命令会得到:

Stand Up
Sit Down
...

多态性简单来说就是“多种形式”。

它不需要继承来实现…作为接口实现,它根本不是继承,服务于多态需求。可以说,接口实现比继承更能满足多态需求。

例如,你会创建一个超类来描述所有会飞的东西吗?我不这么认为。你最好创建一个描述飞行的界面,然后就这样吧。

因此,由于接口描述行为,而方法名描述行为(对程序员来说),将方法重载视为一种较小形式的多态性并不过分。

我觉得你们的概念混在一起了。多态性是一个对象在运行时表现不同的能力。要实现这一点,您需要两个必要条件:

  1. 后期绑定
  2. 继承。

根据你所使用的语言,重载压倒一切的的含义有所不同。例如,在Java中不存在压倒一切的,而是重载。对其基类具有不同签名的重载方法在子类中可用。否则它们将是覆盖(请注意,我的意思是现在没有办法从对象外部调用你的基类方法)。

然而,在c++中却不是这样。任何重载方法,无论签名是否相同(不同的量,不同的类型),都是覆盖。也就是说,当从子类对象外部调用基类的方法时,基类的方法在子类中显然不再可用。

所以答案是,当谈论Java使用重载。在任何其他语言中都可能不同,因为它发生在c++中

意义而言,多态性更有可能……java中的override

这都是关于同一个对象在不同情况下的不同行为(在编程方式中…你可以调用不同的参数)

我认为下面的例子会帮助你理解……虽然它不是纯java代码…

     public void See(Friend)
{
System.out.println("Talk");
}

但是如果我们改变参数…行为将会改变…

     public void See(Enemy)
{
System.out.println("Run");
}

人(这里的“对象”)是一样的……

多态是指多个形式,同一对象根据需求执行不同的操作。

多态可以通过以下两种方式实现

  1. 方法重写
  2. 方法重载

方法重载表示在同一个类中使用相同的方法名编写两个或多个方法,但传递的参数不同。

方法重写意味着我们在不同的类中使用方法名,这意味着父类方法在子类中使用。

在Java中,为了实现多态,父类引用变量可以容纳子类对象。

为了实现多态,每个开发人员必须在项目中使用相同的方法名。

重写更像是通过声明一个与上层方法(超级方法)具有相同名称和签名的方法来隐藏一个继承的方法,这为类添加了多态行为。 换句话说,选择要调用的级别方法的决定将在运行时而不是在编译时做出。 这就引出了接口和实现的概念

多态性是一个对象的多种实现,或者你可以说是一个对象的多种形式。假设你有Animals类作为抽象基类,它有一个叫做movement()的方法,它定义了动物移动的方式。现在在现实中,我们有不同种类的动物,它们的移动方式也不同,有些有两条腿,有些有四条腿,有些没有腿,等等。为了定义地球上每种动物的不同movement(),我们需要应用多态性。然而,你需要定义更多的类,即类Dogs Cats Fish等。然后你需要从基类Animals扩展这些类,并使用基于你拥有的每种动物的新运动功能覆盖它的方法movement()。你也可以使用Interfaces来实现这一点。这里的关键字是覆盖,重载是不同的,不被认为是多态性。通过重载,你可以在同一个对象或类上定义多个“同名”但参数不同的方法。

import java.io.IOException;


class Super {


protected Super getClassName(Super s) throws IOException {
System.out.println(this.getClass().getSimpleName() + " - I'm parent");
return null;
}


}


class SubOne extends Super {


@Override
protected Super getClassName(Super s)  {
System.out.println(this.getClass().getSimpleName() + " - I'm Perfect Overriding");
return null;
}


}


class SubTwo extends Super {


@Override
protected Super getClassName(Super s) throws NullPointerException {
System.out.println(this.getClass().getSimpleName() + " - I'm Overriding and Throwing Runtime Exception");
return null;
}


}


class SubThree extends Super {


@Override
protected SubThree getClassName(Super s) {
System.out.println(this.getClass().getSimpleName()+ " - I'm Overriding and Returning SubClass Type");
return null;
}


}


class SubFour extends Super {


@Override
protected Super getClassName(Super s) throws IOException {
System.out.println(this.getClass().getSimpleName()+ " - I'm Overriding and Throwing Narrower Exception ");
return null;
}


}


class SubFive extends Super {


@Override
public Super getClassName(Super s) {
System.out.println(this.getClass().getSimpleName()+ " - I'm Overriding and have broader Access ");
return null;
}


}


class SubSix extends Super {


public Super getClassName(Super s, String ol) {
System.out.println(this.getClass().getSimpleName()+ " - I'm Perfect Overloading ");
return null;
}


}


class SubSeven extends Super {


public Super getClassName(SubSeven s) {
System.out.println(this.getClass().getSimpleName()+ " - I'm Perfect Overloading because Method signature (Argument) changed.");
return null;
}


}


public class Test{


public static void main(String[] args) throws Exception {


System.out.println("Overriding\n");


Super s1 = new SubOne(); s1.getClassName(null);


Super s2 = new SubTwo(); s2.getClassName(null);


Super s3 = new SubThree(); s3.getClassName(null);


Super s4 = new SubFour(); s4.getClassName(null);


Super s5 = new SubFive(); s5.getClassName(null);


System.out.println("Overloading\n");


SubSix s6 = new SubSix(); s6.getClassName(null, null);


s6 = new SubSix(); s6.getClassName(null);


SubSeven s7 = new SubSeven(); s7.getClassName(s7);


s7 = new SubSeven(); s7.getClassName(new Super());


}
}

什么是多态性?

从java 教程

多态性的字典定义是指生物学中的一个原理,在这个原理中,一个有机体或物种可以有许多不同的形式或阶段。这个原则也可以应用于面向对象编程和Java语言等语言。类的子类可以定义它们自己独特的行为,并且共享父类的一些相同的功能

通过考虑例子和定义,< em >覆盖< / em >应该是可以接受的答案。

关于你的第二个问题:

如果你有一个抽象基类,它定义了一个没有实现的方法,并且你在子类中定义了这个方法,这仍然是覆盖的吗?

它应该被称为重写。

看一下这个例子,了解不同类型的覆盖。

  1. 基类不提供实现,子类必须重写完整方法-(抽象)
  2. 基类提供默认实现,子类可以改变行为
  3. 子类通过调用super.methodName()作为第一条语句向基类实现添加扩展
  4. 基类定义了算法的结构(Template方法),子类将覆盖算法的一部分

代码片段:

import java.util.HashMap;


abstract class Game implements Runnable{


protected boolean runGame = true;
protected Player player1 = null;
protected Player player2 = null;
protected Player currentPlayer = null;


public Game(){
player1 = new Player("Player 1");
player2 = new Player("Player 2");
currentPlayer = player1;
initializeGame();
}


/* Type 1: Let subclass define own implementation. Base class defines abstract method to force
sub-classes to define implementation
*/


protected abstract void initializeGame();


/* Type 2: Sub-class can change the behaviour. If not, base class behaviour is applicable */
protected void logTimeBetweenMoves(Player player){
System.out.println("Base class: Move Duration: player.PlayerActTime - player.MoveShownTime");
}


/* Type 3: Base class provides implementation. Sub-class can enhance base class implementation by calling
super.methodName() in first line of the child class method and specific implementation later */
protected void logGameStatistics(){
System.out.println("Base class: logGameStatistics:");
}
/* Type 4: Template method: Structure of base class can't be changed but sub-class can some part of behaviour */
protected void runGame() throws Exception{
System.out.println("Base class: Defining the flow for Game:");
while ( runGame) {
/*
1. Set current player
2. Get Player Move
*/
validatePlayerMove(currentPlayer);
logTimeBetweenMoves(currentPlayer);
Thread.sleep(500);
setNextPlayer();
}
logGameStatistics();
}
/* sub-part of the template method, which define child class behaviour */
protected abstract void validatePlayerMove(Player p);


protected void setRunGame(boolean status){
this.runGame = status;
}
public void setCurrentPlayer(Player p){
this.currentPlayer = p;
}
public void setNextPlayer(){
if ( currentPlayer == player1) {
currentPlayer = player2;
}else{
currentPlayer = player1;
}
}
public void run(){
try{
runGame();
}catch(Exception err){
err.printStackTrace();
}
}
}


class Player{
String name;
Player(String name){
this.name = name;
}
public String getName(){
return name;
}
}


/* Concrete Game implementation  */
class Chess extends Game{
public Chess(){
super();
}
public void initializeGame(){
System.out.println("Child class: Initialized Chess game");
}
protected void validatePlayerMove(Player p){
System.out.println("Child class: Validate Chess move:"+p.getName());
}
protected void logGameStatistics(){
super.logGameStatistics();
System.out.println("Child class: Add Chess specific logGameStatistics:");
}
}
class TicTacToe extends Game{
public TicTacToe(){
super();
}
public void initializeGame(){
System.out.println("Child class: Initialized TicTacToe game");
}
protected void validatePlayerMove(Player p){
System.out.println("Child class: Validate TicTacToe move:"+p.getName());
}
}


public class Polymorphism{
public static void main(String args[]){
try{


Game game = new Chess();
Thread t1 = new Thread(game);
t1.start();
Thread.sleep(1000);
game.setRunGame(false);
Thread.sleep(1000);


game = new TicTacToe();
Thread t2 = new Thread(game);
t2.start();
Thread.sleep(1000);
game.setRunGame(false);


}catch(Exception err){
err.printStackTrace();
}
}
}

输出:

Child class: Initialized Chess game
Base class: Defining the flow for Game:
Child class: Validate Chess move:Player 1
Base class: Move Duration: player.PlayerActTime - player.MoveShownTime
Child class: Validate Chess move:Player 2
Base class: Move Duration: player.PlayerActTime - player.MoveShownTime
Base class: logGameStatistics:
Child class: Add Chess specific logGameStatistics:
Child class: Initialized TicTacToe game
Base class: Defining the flow for Game:
Child class: Validate TicTacToe move:Player 1
Base class: Move Duration: player.PlayerActTime - player.MoveShownTime
Child class: Validate TicTacToe move:Player 2
Base class: Move Duration: player.PlayerActTime - player.MoveShownTime
Base class: logGameStatistics:

虽然,多态性已经在这篇文章中解释了很多细节,但我想更强调为什么它的一部分。

为什么多态性在任何面向对象语言中都如此重要。

让我们尝试为一个有或没有继承/多态性的电视构建一个简单的应用程序。在发布每个版本的应用程序后,我们会做一个小型回顾。

假设你是一家电视公司的软件工程师,你被要求为音量、亮度和颜色控制器编写软件,根据用户命令增加或减少它们的值。

首先,您可以通过添加来为每个特性编写类

  1. set:—设置控制器的值。(假设它有特定于控制器的代码)
  2. get:—获取控制器的值。(假设它有特定于控制器的代码)
  3. 调整:—验证输入和设置控制器。(通用验证. .独立于控制器)
  4. 用户输入映射到控制器:—获取用户输入并相应调用控制器。

应用程序版本1

import java.util.Scanner;
class VolumeControllerV1 {
private int value;
int get()    {
return value;
}
void set(int value) {
System.out.println("Old value of VolumeController \t"+this.value);
this.value = value;
System.out.println("New value of VolumeController \t"+this.value);
}
void adjust(int value)  {
int temp = this.get();
if(((value > 0) && (temp >= 100)) || ((value < 0) && (temp <= 0)))    {
System.out.println("Can not adjust any further");
return;
}
this.set(temp + value);
}
}
class  BrightnessControllerV1 {
private int value;
int get()    {
return value;
}
void set(int value) {
System.out.println("Old value of BrightnessController \t"+this.value);
this.value = value;
System.out.println("New value of BrightnessController \t"+this.value);
}
void adjust(int value)  {
int temp = this.get();
if(((value > 0) && (temp >= 100)) || ((value < 0) && (temp <= 0)))    {
System.out.println("Can not adjust any further");
return;
}
this.set(temp + value);
}
}
class ColourControllerV1    {
private int value;
int get()    {
return value;
}
void set(int value) {
System.out.println("Old value of ColourController \t"+this.value);
this.value = value;
System.out.println("New value of ColourController \t"+this.value);
}
void adjust(int value)  {
int temp = this.get();
if(((value > 0) && (temp >= 100)) || ((value < 0) && (temp <= 0)))    {
System.out.println("Can not adjust any further");
return;
}
this.set(temp + value);
}
}


/*
*       There can be n number of controllers
* */
public class TvApplicationV1 {
public static void main(String[] args)  {
VolumeControllerV1 volumeControllerV1 = new VolumeControllerV1();
BrightnessControllerV1 brightnessControllerV1 = new BrightnessControllerV1();
ColourControllerV1 colourControllerV1 = new ColourControllerV1();




OUTER: while(true) {
Scanner sc=new Scanner(System.in);
System.out.println(" Enter your option \n Press 1 to increase volume \n Press 2 to decrease volume");
System.out.println(" Press 3 to increase brightness \n Press 4 to decrease brightness");
System.out.println(" Press 5 to increase color \n Press 6 to decrease color");
System.out.println("Press any other Button to shutdown");
int button = sc.nextInt();
switch (button) {
case  1:    {
volumeControllerV1.adjust(5);
break;
}
case 2: {
volumeControllerV1.adjust(-5);
break;
}
case  3:    {
brightnessControllerV1.adjust(5);
break;
}
case 4: {
brightnessControllerV1.adjust(-5);
break;
}
case  5:    {
colourControllerV1.adjust(5);
break;
}
case 6: {
colourControllerV1.adjust(-5);
break;
}
default:
System.out.println("Shutting down...........");
break OUTER;
}


}
}
}

现在,您已经准备好部署工作应用程序的第一个版本了。该分析到目前为止所做的工作了。

TV应用程序版本1中的问题

  1. 调整(int值)代码在所有三个类中都是重复的。您希望最小化代码表里不一。(但是您没有想到通用代码并将其移动到某个超类以避免重复代码)

只要应用程序按预期工作,您就决定接受这种情况。

有时候,你的Boss会回来找你,要求你在现有的应用程序中添加重置功能。Reset会将所有3个控制器设置为各自的默认值。

您开始为新功能编写一个新类(ResetFunctionV2),并为这个新功能映射用户输入映射代码。

应用程序版本2

import java.util.Scanner;
class VolumeControllerV2    {


private int defaultValue = 25;
private int value;


int getDefaultValue() {
return defaultValue;
}
int get()    {
return value;
}
void set(int value) {
System.out.println("Old value of VolumeController \t"+this.value);
this.value = value;
System.out.println("New value of VolumeController \t"+this.value);
}
void adjust(int value)  {
int temp = this.get();
if(((value > 0) && (temp >= 100)) || ((value < 0) && (temp <= 0)))    {
System.out.println("Can not adjust any further");
return;
}
this.set(temp + value);
}
}
class  BrightnessControllerV2   {


private int defaultValue = 50;
private int value;
int get()    {
return value;
}
int getDefaultValue() {
return defaultValue;
}
void set(int value) {
System.out.println("Old value of BrightnessController \t"+this.value);
this.value = value;
System.out.println("New value of BrightnessController \t"+this.value);
}
void adjust(int value)  {
int temp = this.get();
if(((value > 0) && (temp >= 100)) || ((value < 0) && (temp <= 0)))    {
System.out.println("Can not adjust any further");
return;
}
this.set(temp + value);
}
}
class ColourControllerV2    {


private int defaultValue = 40;
private int value;
int get()    {
return value;
}
int getDefaultValue() {
return defaultValue;
}
void set(int value) {
System.out.println("Old value of ColourController \t"+this.value);
this.value = value;
System.out.println("New value of ColourController \t"+this.value);
}
void adjust(int value)  {
int temp = this.get();
if(((value > 0) && (temp >= 100)) || ((value < 0) && (temp <= 0)))    {
System.out.println("Can not adjust any further");
return;
}
this.set(temp + value);
}
}


class ResetFunctionV2 {


private VolumeControllerV2 volumeControllerV2 ;
private BrightnessControllerV2 brightnessControllerV2;
private ColourControllerV2 colourControllerV2;


ResetFunctionV2(VolumeControllerV2 volumeControllerV2, BrightnessControllerV2 brightnessControllerV2, ColourControllerV2 colourControllerV2)  {
this.volumeControllerV2 = volumeControllerV2;
this.brightnessControllerV2 = brightnessControllerV2;
this.colourControllerV2 = colourControllerV2;
}
void onReset()    {
volumeControllerV2.set(volumeControllerV2.getDefaultValue());
brightnessControllerV2.set(brightnessControllerV2.getDefaultValue());
colourControllerV2.set(colourControllerV2.getDefaultValue());
}
}
/*
*       so on
*       There can be n number of controllers
*
* */
public class TvApplicationV2 {
public static void main(String[] args)  {
VolumeControllerV2 volumeControllerV2 = new VolumeControllerV2();
BrightnessControllerV2 brightnessControllerV2 = new BrightnessControllerV2();
ColourControllerV2 colourControllerV2 = new ColourControllerV2();


ResetFunctionV2 resetFunctionV2 = new ResetFunctionV2(volumeControllerV2, brightnessControllerV2, colourControllerV2);


OUTER: while(true) {
Scanner sc=new Scanner(System.in);
System.out.println(" Enter your option \n Press 1 to increase volume \n Press 2 to decrease volume");
System.out.println(" Press 3 to increase brightness \n Press 4 to decrease brightness");
System.out.println(" Press 5 to increase color \n Press 6 to decrease color");
System.out.println(" Press 7 to reset TV \n Press any other Button to shutdown");
int button = sc.nextInt();
switch (button) {
case  1:    {
volumeControllerV2.adjust(5);
break;
}
case 2: {
volumeControllerV2.adjust(-5);
break;
}
case  3:    {
brightnessControllerV2.adjust(5);
break;
}
case 4: {
brightnessControllerV2.adjust(-5);
break;
}
case  5:    {
colourControllerV2.adjust(5);
break;
}
case 6: {
colourControllerV2.adjust(-5);
break;
}
case 7: {
resetFunctionV2.onReset();
break;
}
default:
System.out.println("Shutting down...........");
break OUTER;
}


}
}
}

因此,您已经为您的应用程序准备好了Reset特性。但是,现在你开始意识到

TV应用程序版本2中的问题

  1. 如果产品中引入了新的控制器,则必须更改Reset特性代码。
  2. 如果控制器的计数增长非常高,你将有问题在持有控制器的引用。
  3. 重置特性代码与所有控制器类的代码紧密耦合(以获取和设置默认值)。
  4. 重置功能类(ResetFunctionV2)可以访问控制器类的其他方法(调整),这是不可取的。

与此同时,你从你的老板那里听说,你可能必须添加一个功能,其中每个控制器在启动时,需要通过互联网从公司的托管驱动程序库中检查最新版本的驱动程序。

现在,您开始认为这个要添加的新功能类似于Reset功能,如果您不重新分解应用程序,应用程序问题(V2)将成倍增加。

您开始考虑使用继承,以便能够利用JAVA的多态能力,并添加一个新的抽象类(ControllerV3)

  1. 声明get和set方法的签名。
  2. 包含调整方法实现,该方法之前在所有控制器中复制。
  3. 声明setDefault方法,以便利用多态性轻松实现重置功能。

有了这些改进,您就有了TV应用程序的版本3。

应用程序版本3

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;


abstract class ControllerV3 {
abstract void set(int value);
abstract int get();
void adjust(int value)  {
int temp = this.get();
if(((value > 0) && (temp >= 100)) || ((value < 0) && (temp <= 0)))    {
System.out.println("Can not adjust any further");
return;
}
this.set(temp + value);
}
abstract void setDefault();
}
class VolumeControllerV3 extends ControllerV3   {


private int defaultValue = 25;
private int value;


public void setDefault() {
set(defaultValue);
}
int get()    {
return value;
}
void set(int value) {
System.out.println("Old value of VolumeController \t"+this.value);
this.value = value;
System.out.println("New value of VolumeController \t"+this.value);
}
}
class  BrightnessControllerV3  extends ControllerV3   {


private int defaultValue = 50;
private int value;


public void setDefault() {
set(defaultValue);
}
int get()    {
return value;
}
void set(int value) {
System.out.println("Old value of BrightnessController \t"+this.value);
this.value = value;
System.out.println("New value of BrightnessController \t"+this.value);
}
}
class ColourControllerV3 extends ControllerV3   {


private int defaultValue = 40;
private int value;


public void setDefault() {
set(defaultValue);
}
int get()    {
return value;
}
void set(int value) {
System.out.println("Old value of ColourController \t"+this.value);
this.value = value;
System.out.println("New value of ColourController \t"+this.value);
}
}


class ResetFunctionV3 {


private List<ControllerV3> controllers = null;


ResetFunctionV3(List<ControllerV3> controllers)  {
this.controllers = controllers;
}
void onReset()    {
for (ControllerV3 controllerV3 :this.controllers)  {
controllerV3.setDefault();
}
}
}
/*
*       so on
*       There can be n number of controllers
*
* */
public class TvApplicationV3 {
public static void main(String[] args)  {
VolumeControllerV3 volumeControllerV3 = new VolumeControllerV3();
BrightnessControllerV3 brightnessControllerV3 = new BrightnessControllerV3();
ColourControllerV3 colourControllerV3 = new ColourControllerV3();


List<ControllerV3> controllerV3s = new ArrayList<>();
controllerV3s.add(volumeControllerV3);
controllerV3s.add(brightnessControllerV3);
controllerV3s.add(colourControllerV3);


ResetFunctionV3 resetFunctionV3 = new ResetFunctionV3(controllerV3s);


OUTER: while(true) {
Scanner sc=new Scanner(System.in);
System.out.println(" Enter your option \n Press 1 to increase volume \n Press 2 to decrease volume");
System.out.println(" Press 3 to increase brightness \n Press 4 to decrease brightness");
System.out.println(" Press 5 to increase color \n Press 6 to decrease color");
System.out.println(" Press 7 to reset TV \n Press any other Button to shutdown");
int button = sc.nextInt();
switch (button) {
case  1:    {
volumeControllerV3.adjust(5);
break;
}
case 2: {
volumeControllerV3.adjust(-5);
break;
}
case  3:    {
brightnessControllerV3.adjust(5);
break;
}
case 4: {
brightnessControllerV3.adjust(-5);
break;
}
case  5:    {
colourControllerV3.adjust(5);
break;
}
case 6: {
colourControllerV3.adjust(-5);
break;
}
case 7: {
resetFunctionV3.onReset();
break;
}
default:
System.out.println("Shutting down...........");
break OUTER;
}


}
}
}

虽然V2问题列表中列出的大部分问题都得到了解决

TV应用程序版本3中的问题

  1. 重置功能类(ResetFunctionV3)可以访问控制器类的其他方法(调整),这是不可取的。

同样,您考虑解决这个问题,因为现在还有另一个特性(启动时更新驱动程序)需要实现。如果你不修复它,它也会被复制到新的功能上。

因此,将抽象类中定义的契约划分为两个接口

  1. 重置功能。
  2. 驱动程序更新。

并让你的第一个具体类实现如下所示

应用程序版本4

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;


interface OnReset {
void setDefault();
}
interface OnStart {
void checkForDriverUpdate();
}
abstract class ControllerV4 implements OnReset,OnStart {
abstract void set(int value);
abstract int get();
void adjust(int value)  {
int temp = this.get();
if(((value > 0) && (temp >= 100)) || ((value < 0) && (temp <= 0)))    {
System.out.println("Can not adjust any further");
return;
}
this.set(temp + value);
}
}


class VolumeControllerV4 extends ControllerV4 {


private int defaultValue = 25;
private int value;
@Override
int get()    {
return value;
}
void set(int value) {
System.out.println("Old value of VolumeController \t"+this.value);
this.value = value;
System.out.println("New value of VolumeController \t"+this.value);
}
@Override
public void setDefault() {
set(defaultValue);
}


@Override
public void checkForDriverUpdate()    {
System.out.println("Checking driver update for VolumeController .... Done");
}
}
class  BrightnessControllerV4 extends ControllerV4 {


private int defaultValue = 50;
private int value;
@Override
int get()    {
return value;
}
@Override
void set(int value) {
System.out.println("Old value of BrightnessController \t"+this.value);
this.value = value;
System.out.println("New value of BrightnessController \t"+this.value);
}


@Override
public void setDefault() {
set(defaultValue);
}


@Override
public void checkForDriverUpdate()    {
System.out.println("Checking driver update for BrightnessController .... Done");
}
}
class ColourControllerV4 extends ControllerV4 {


private int defaultValue = 40;
private int value;
@Override
int get()    {
return value;
}
void set(int value) {
System.out.println("Old value of ColourController \t"+this.value);
this.value = value;
System.out.println("New value of ColourController \t"+this.value);
}
@Override
public void setDefault() {
set(defaultValue);
}


@Override
public void checkForDriverUpdate()    {
System.out.println("Checking driver update for ColourController .... Done");
}
}
class ResetFunctionV4 {


private List<OnReset> controllers = null;


ResetFunctionV4(List<OnReset> controllers)  {
this.controllers = controllers;
}
void onReset()    {
for (OnReset onreset :this.controllers)  {
onreset.setDefault();
}
}
}
class InitializeDeviceV4 {


private List<OnStart> controllers = null;


InitializeDeviceV4(List<OnStart> controllers)  {
this.controllers = controllers;
}
void initialize()    {
for (OnStart onStart :this.controllers)  {
onStart.checkForDriverUpdate();
}
}
}
/*
*       so on
*       There can be n number of controllers
*
* */
public class TvApplicationV4 {
public static void main(String[] args)  {
VolumeControllerV4 volumeControllerV4 = new VolumeControllerV4();
BrightnessControllerV4 brightnessControllerV4 = new BrightnessControllerV4();
ColourControllerV4 colourControllerV4 = new ColourControllerV4();
List<ControllerV4> controllerV4s = new ArrayList<>();
controllerV4s.add(brightnessControllerV4);
controllerV4s.add(volumeControllerV4);
controllerV4s.add(colourControllerV4);


List<OnStart> controllersToInitialize = new ArrayList<>();
controllersToInitialize.addAll(controllerV4s);
InitializeDeviceV4 initializeDeviceV4 = new InitializeDeviceV4(controllersToInitialize);
initializeDeviceV4.initialize();


List<OnReset> controllersToReset = new ArrayList<>();
controllersToReset.addAll(controllerV4s);
ResetFunctionV4 resetFunctionV4 = new ResetFunctionV4(controllersToReset);


OUTER: while(true) {
Scanner sc=new Scanner(System.in);
System.out.println(" Enter your option \n Press 1 to increase volume \n Press 2 to decrease volume");
System.out.println(" Press 3 to increase brightness \n Press 4 to decrease brightness");
System.out.println(" Press 5 to increase color \n Press 6 to decrease color");
System.out.println(" Press 7 to reset TV \n Press any other Button to shutdown");
int button = sc.nextInt();
switch (button) {
case  1:    {
volumeControllerV4.adjust(5);
break;
}
case 2: {
volumeControllerV4.adjust(-5);
break;
}
case  3:    {
brightnessControllerV4.adjust(5);
break;
}
case 4: {
brightnessControllerV4.adjust(-5);
break;
}
case  5:    {
colourControllerV4.adjust(5);
break;
}
case 6: {
colourControllerV4.adjust(-5);
break;
}
case 7: {
resetFunctionV4.onReset();
break;
}
default:
System.out.println("Shutting down...........");
break OUTER;
}


}
}
}

现在,您所面临的所有问题都得到了解决,并且您意识到使用继承和多态性可以实现

  1. 保持应用程序的各个部分松散耦合。(重置或驱动更新功能组件不需要知道实际的控制器类(音量,亮度和颜色),任何实现OnReset或OnStart的类将分别被重置或驱动更新功能组件接受)。
  2. 应用程序增强变得更加容易。(新添加的控制器不会影响重置或驱动更新功能组件,现在真的很容易为你添加新的)
  3. 保持抽象层。(现在Reset功能只能看到控制器的setDefault方法,Reset功能只能看到控制器的checkForDriverUpdate方法)

希望,这有帮助:-)