Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
重构具有多个子类的抽象Java类_Java_Design Patterns_Refactoring_Abstract Class - Fatal编程技术网

重构具有多个子类的抽象Java类

重构具有多个子类的抽象Java类,java,design-patterns,refactoring,abstract-class,Java,Design Patterns,Refactoring,Abstract Class,我正在寻找重构这个场景的最佳方法,更好的设计,最少的努力。 从以下示例开始,抽象类actual有更多的字段、方法和抽象方法: abstract class Car { private int manufactureYear; // ... many more fields that are hard to clone public Car(int manYear) { this.manufactureYear = manYear; }

我正在寻找重构这个场景的最佳方法,更好的设计,最少的努力。 从以下示例开始,抽象类actual有更多的字段、方法和抽象方法:

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;
    }
}
在运行期间,将实例化和使用car对象:

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。问题是如何最小化对那些100多个子类的更改代码,最好让它们保持不变,同时能够在运行时更新carObject


注意:我被赋予处理此代码的任务,因此我没有在这种情况下开始编写此代码。

根据描述和示例,您使用继承的方式不正确。看起来您正在创建许多类,其中您应该使用单个类和许多对象实例。如果这是真的,您也不需要设计模式来解决问题。在不进一步澄清问题的情况下,这就足够了:

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
}
Car carOne = CarFactory.makeCar("CarOne", 2009);

因为实际上有一个基类,我猜99%的代码并没有引用具体的car类,只是引用了基类,所以您应该能够相当容易地更改内容。当然,如果看不到真正的代码,很难说。

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

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
}
Car carOne = CarFactory.makeCar("CarOne", 2009);

因为实际上有一个基类,我猜99%的代码并没有引用具体的car类,只是引用了基类,所以您应该能够相当容易地更改内容。当然,很难说没有看到真正的代码。

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

如果您可以控制它们的创建,那么使用组合并返回不同类型的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
}
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; }
}

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

如果您可以控制它们的创建,那么使用组合并返回不同类型的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
}
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方法,并将其用作工厂meth od

添加颜色和圆柱体属性并从中加载它们。。。任何适合您需要的地方,它都可能是一个数据库、一个属性文件、一个模拟对象、来自互联网、来自宇宙的地方。。。等等

之前:

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 );
为了


其余的代码应该在不做任何更改的情况下运行

您的子类不提供不同的行为,只提供不同的数据

因此,您不应该只使用不同的参数来使用不同的子类

我建议将getCar方法添加到基本案例中,并将其用作工厂方法

添加颜色和圆柱体属性并从中加载它们。。。任何适合您需要的地方,它都可能是一个数据库、一个属性文件、一个模拟对象、来自互联网、来自宇宙的地方。。。等等

之前:

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 );
为了


其余的代码应该在不做任何更改的情况下运行

挑剔:应该是:blue6.setEngineEngine.V6;因为气缸实际上是发动机的属性,而不是汽车的属性。有些属性是不可变的,如manufactureYear,并且在创建对象后无法更改。该类不应为此类属性公开setter。我大体上同意setter,除非您希望遵循JavaBean规范,由于需要非参数化构造函数,因此参数化构造函数除了提供便利外,还会失去其价值。关于引擎-当然,如果他建模的引擎细节超出了气缸数,或者需要策略模式或类似的模式,但在这里,他似乎只需要一个没有更多细节的整型整数。吹毛求疵:应该是:blue6.setEngineEngine.V6;因为气缸实际上是发动机的属性,而不是汽车的属性。有些属性是不可变的,如manufactureYear,并且在创建对象后无法更改。该类不应为此类属性公开setter。我大体上同意setter,除非您希望遵循JavaBean规范,由于需要非参数化构造函数,因此参数化构造函数除了提供便利外,还会失去其价值。关于引擎-当然,如果他建模的引擎细节超出了气缸数,或者需要一个策略模式或类似的模式,但这里看起来他只需要一个平坦的int,而不需要进一步的细节。