使用许多子类重构抽象Java类

我正在寻找关于重构这种情况的最佳方法的想法(更好的设计,最小的努力)。 从以下示例开始抽象类(实际有更多字段,方法和抽象方法):

abstract class Car { private int manufactureYear; // ... many more fields that are hard to clone public Car(int manYear) { this.manufactureYear = manYear; } abstract public Color getColor(); abstract public int getNumCylinders(); } 

有很多子类(比如100)扩展了这个类。 这些儿童课程被认为是汽车的“规格”。 以下是两个例子:

 class CarOne extends Car { private static Color COLOR = Color.Red; private static int CYLINDERS = 4; public CarOne(int manYear) { super(manYear); } public final Color getColor(); { return COLOR; } public final int getNumCylinders() { return CYLINDERS; } } class CarOneThousand extends Car { private static Color COLOR = Color.Black; private static int CYLINDERS = 6; public CarOneThousand(int manYear) { super(manYear); } public final Color getColor(); { return COLOR; } public final int getNumCylinders() { return CYLINDERS; } } 

在运行时期间,汽车对象被实例化并使用:

 CarOne carObject = new CarOne(2009); carObject.getColor(); carObject.getNumCylinders(); 

然而,在获得一些外部数据后,我发现汽车被重新粉刷,发动机也发生了变化。 汽车的新规格变为:

 class ModCar extends Car { private static Color COLOR = Color.Blue; private static int numCylinders = 8; public ModCar (int manYear) { super(manYear); } public final Color getColor(); { return COLOR; } public final int getNumCylinders() { return numCylinders; } } 

因此,确实需要将这些规范“应用”到新的carObject而无需修改现有字段,例如manufactureDate 。 问题是如何在运行时更新carObject同时最小化那些100多个子类的更改代码(最好不要触及它们)。

注意我被赋予了这个代码的工作,所以我没有在这个条件下写它开始。

根据描述和示例,您不恰当地使用inheritance。 看起来您正在创建许多类,您应该使用单个类和许多对象实例。 如果是这样,您也不需要设计模式来解决问题。 如果没有进一步澄清问题,这应该足够了:

 class Car { private int manufactureYear; private Color color; private int numCylinders; public int getManufactureYear() { return manufactureYear; } public void setManufactureYear(int manufactureYear) { this.manufactureYear = manufactureYear; } public Color getColor() { return color; } public void setColor(Color color) { this.color = color; } public int getNumCylinders() { return numCylinders; } public void setNumCylinders(int numCylinders) { this.numCylinders = numCylinders; } } 

示例用法:

 // make a blue 6-cylinder: Car blue6 = new Car(); blue6.setColor(BLUE); blue6.setCylinders(6); // make a red 4-cylinder: Car red4 = new Car(); red4.setColor(RED); red4.setCylinders(4); // Uh-oh, they painted my red car! red4.setColor(YELLOW); 

如果要最小化更改,可以使用上面重构的Car类,然后清理子类以便它们利用它。 就像是:

 class CarOne extends Car { // extends my version of Car... private static Color COLOR = Color.Red; private static int CYLINDERS = 4; public CarOne() { setColor(COLOR); setNumCylinders(CYLINDERS ); } // getters deleted, base class has them now } 

由于实际上有一个基类,我的猜测是99%的代码没有引用具体的汽车类(只有基类),所以你应该能够相当容易地改变事物。 当然,很难说没有看到真正的代码。

这取决于您对创建这些对象的代码的控制程度。 我将假设这个设计存在的原因在汽车示例中有点丢失,但是如果通过调用new创建对象,那么除了更改它们之外几乎没有什么可以做的,尽管你可以使用其余的答案建议采用更灵活的方式来改变它们。

如果你可以控制他们的创建,那么一个使用合成并返回一个不同类型的汽车对象的工厂会覆盖你关心的特定参数并调用其他的原始参数,这将允许你影响你对特定实例的更改而不改变所有原来的课程。 就像是:

 Car carOne = CarFactory.makeCar("CarOne", 2009); 

然后在makeCar方法中,您可以决定是否返回CarOne对象或复合实现:

 public class CompositeCar extends Car { private Car original; private Color myColor; public CompositeCar(Car original, Color myColor) { this.original = original; this.myColor = myColor; } public int getYear() { return original.getYear(); } public Color getColor() { return myColor; } } 

如果您有具有复杂构造逻辑的案例(或整个类组),我还建议您查看构建器模式 ,特别是如果某些汽车需要某些字段,而其他字段需要不同的字段集。

您的子类不会仅为不同的数据提供不同的行为

因此,您不应该只使用不同的子类。

我建议在你的基础案例中添加一个“getCar”方法,并将其用作工厂方法。

添加Color和Cylinder属性并从……加载它们适合您的需要,它可能是数据库,属性文件,模拟对象,来自互联网,来自宇宙的地方……等等。

之前:

 Car car = new CarOne(2009); // Using new to get different data.... carObject.getColor(); carObject.getNumCylinders(); 

后:

 class Car { // Attributes added and marked as final. private final Color color; private final int numberCylinders; // original private final int manufacteredYear; public static Car getCar( String spec, int year ) { return new Car( year, getColorFor( spec ) , getCylindersFor(spec) ); } // Make this private so only the static method do create cars. private Car( int year, Color color, int cylinders ) { this.manufacturedYear = year; this.color = color; this.numberCylinders = cylinders; } // Utility methods to get values for the car spec. private static final getColorFor( String spec ) { // fill either from db, xml, textfile, propertie, resource bundle, or hardcode here!!! return .... } private static final getCylindersFor( String spec ) { // fill either from db, xml, textfile, propertie, resource bundle, or hardcode here!!! return .... } // gettes remain the same, only they are not abstract anymore. public Color getColor(){ return this.color; } public int getNumCylinders(){ return this.numberCylinders; } } 

因此,不是直接创建新车,而是从getCar方法获取它:

 Car car = Car.getCar("CarOne", 2009 ); .... 

我不建议你让你的汽车“可变”,因为它可能带来微妙的不良副作用(这就是为什么我将属性标记为最终)。 因此,如果您需要“修改”您的汽车,您最好分配新属性:

  Car myCar = Car.getCar("XYZ", 2009 ); .... do something with car myCar = Car.getCar("Modified", 2009 ); //-- engine and color are "modified" 

此外,您甚至可以映射整辆车,因此您只使用一个实例。

通过这样做,您不必在代码中添加setter。 你唯一要做的就是搜索和替换

  Car xyz = new WhatEver( number ); 

对于

  Car xyz = Car.getCar("WhatEver", number ); 

其余的代码应该无需更改即可运行。