抽象类的集合(或类似的东西......)

时间:2011-09-21 00:52:05

标签: java inheritance collections enums subclass

情景

我正在制作一个涉及汽车的Java程序。

注意:我已经简化了这种情况(尽我所能),使其更通用,更容易理解。我实际上并不是在开车。

我创建了一个Cars类,它是Car个对象的集合。

Car对象有speed(双)和year(int)。构造函数将year作为参数,例如:

public class Car {
    private int year;
    private double speed;

    public Car(int year) {
        this.year = year;
    }
}

这是棘手的部分......汽车必须有一种(让我们说Corvette或Clunker)。 Corvette将speed 0.9,而Clunker将speed 0.1。如果没有指定它应该是什么类型的汽车,Car永远不会被实例化。所以,现在,要创造一辆汽车,我们有:

Car car = new Car(1998, Corvette);

我们刚刚创建的Corvette将是一个Car个对象,speed0.9

问题

我的实际情况涉及更多种类的汽车,每辆汽车除了speed外还有几个特定属性(可能还有colornumDoorsfuelTankSize的字段)。有这么多种车(每种都有自己的特定属性),代码变得比我想要的更复杂。

可能的解决方案

  1. 我可以使用子类,也就是说,有一个由CarCorvette扩展的抽象Clunker类,但后来我遇到了使用{的问题{1}}对象(因为我无法创建无法实例化的东西的集合)。 请参阅下面的编辑。

  2. 使用枚举(例如Cars)似乎需要几个混乱的切换语句:

    • 填充每辆车的CarKind字段
    • speed
    • 创建Car个对象
  3. 如何提供帮助

    我正在寻找一种允许单个Cars类包含每个Cars对象的解决方案。我想要不同的集合(例如CarCorvettes)。我也正在寻找一种解决方案,允许根据单个汽车类型的属性创建Clunkers个对象......如前所述,创建一个新的Car种类Car会产生Corvette speed。应该没有其他方法来指定汽车的0.9

    在这种情况下是否有最佳做法?我让这个例子足够清楚吗?

    感谢。

    编辑:我不想要收集抽象speed对象的原因是因为Car集合的要点是创建和操作Cars个对象,无论它们的种类如何。 Car抽象似乎使这变得复杂。如果您认为这是最佳解决方案,请相应地回答。

4 个答案:

答案 0 :(得分:10)

  

我正在寻找一种允许单个Cars类包含的解决方案   每个Car对象。我不想要不同的收藏品(如Corvettes,   旧车)。我也在寻找一种允许创建的解决方案   汽车对象基于单个汽车的属性...作为   前面提到过,创造一种新型的Corvette车将会产生   速度为0.9。应该没有其他方法来指定汽车   速度。

哦,男孩哦,男孩,有很多方法可以解决这个问题,我们可以整天继续!我会做一次大脑转储,希望你处理它不会太多。


解决方案1:使用策略。

策略基本上是将重度可替代逻辑与另一个类分开的一种方法。在这种情况下,每辆汽车都需要以不同的方式创建。一个策略是完美的。

很抱歉,如果我偶然混入一些C#...自从我编写java以来​​已经很久了。

public interface CarCreationStrategy{
   void BuildCar(Car theCar);
}

public class CorvetteStrategy implements CarCreationStrategy{
   public void BuildCar(Car theCar){
      theCar.Type = "Corvette";
      theCar.Speed = 0.9;
      theCar.Comments = "Speedster!";
   }
}

public class ToyotaStrategy implements CarCreationStrategy{
   public void BuildCar(Car theCar){
      theCar.Type = "Toyota";
      theCar.Speed = "0.5";
      theCar.Comments = "Never dies, even if you drop it from the top of a building";
   }
}

现在,您可以使用汽车构造函数传递策略。

public class Car{
   // Variables ...

   public Car(CarCreationStrategy strategy, int year){
      strategy.BuildCar(this); // Implements your properties here.
      this.year = year;
   }
}

所以,你现在得到的真是太棒了!

List<Car> cars = new List<Car>();
cars.Add(new Car(new CorvetteStrategy(),1999));
cars.Add(new Car(new ToyotaStrategy(),2011);

这将完全符合您的要求。

但是,你会在战略与汽车之间找到一种耦合。


解决方案2:使用工厂。

工厂也是一个很好的解决方案,而且可能更容易。你所做的是拥有一个CarFactory,有多种工厂方法来创建每种类型的汽车。

public class CarFactory{
   public static Car BuildCorvette(int year){
      Car car = new Car(year);
      car.Type = "Corvette;
      car.Speed = 0.9";
      return car;
   }

   public static Car BuildToyota(int year){
      Car car = new Car(year);
      car.Type = "Toyota;
      car.Speed = 0.5";
      return car;
   }
}

用法:

List<Car> cars = new List<Car>();
cars.Add(CarFactory.BuildCorvette(1999));
cars.Add(CarFactory.BuildToyota(2011));

所以关于这一点的好处是你不必担心现在实例化汽车。它全部由CarFactory处理,将您的“实例化逻辑”与您的代码分离。但是,您仍然需要知道要构建哪辆车并相应地调用该方法,这仍然是一个小耦合。


解决方案3:策略工厂!

所以,如果我们想要摆脱最后一点的耦合,让我们将两者结合起来!

public class CarFactory{
   public static Car BuildCar(CarCreationStrategy strategy, int year){
      Car car = new Car(year);
      strategy.BuildCar(car);
      return car;
   }
}

List<Car> cars = new List<Car>();
cars.Add(CarFactory.BuildCar(new CorvetteStrategy(),1999));
cars.Add(CarFactory.BuildCar(new ToyotaStrategy(),2011);

现在你有一个建造汽车的战略,一个为你建造它们的工厂,以及一辆没有你原来的额外联轴器的汽车。很棒,不是吗?

如果你使用过Swing,你会注意到这就是他们处理一些事情的方式,比如Layouts(GridBagLayout,GridLayout都是策略)。还有一个BorderFactory。


改进

抽象策略

public interface CarCreationStrategy{
   void BuildCar(Car theCar);
}

public class AbstractStrategy:CarCreationStrategy{
   public string Type;
   public double Speed;
   public string Comments;

   public void BuildCar(Car theCar){
       theCar.Type = this.Type;
       theCar.Speed = this.Speed;
       theCar.Comments = this.Comments;
   }
}

public class CorvetteStrategy extends AbstractStrategy{
   public CorvetteStrategy(){
      this.Type = "Corvette";
      this.Speed = 0.9;
      this.Comments = "Speedster!";
   }
}

public class ToyotaStrategy extends AbstractStrategy{
   public ToyotaStrategy{
      this.Type = "Toyota";
      this.Speed = "0.5";
      this.Comments = "Never dies, even if you drop it from the top of a building";
   }
}

使用此功能,您可以灵活地动态创建AbstractStrategies(例如,从数据存储中提取汽车属性)。

答案 1 :(得分:3)

我使用了Daryl Teo's answer中描述的方法,但我稍微修改了它以包含枚举(因为有一定数量的单个汽车)。因为他的答案对我的最终解决方案的发展非常有帮助,所以我选择它作为最佳答案。

CarCreationStrategy界面:

public interface CarCreationStrategy {
    public void buildCar(Car car);
}

enum CarTypes implements CarCreationStrategy{
    CORVETTE() {
        @Override
        public String toString() {
            return "Corvette";
        }

        public void buildCar(Car car) {
            car.setSpeed (0.9);
        }
    },
    CLUNKER() {
        @Override
        public String toString() {
            return "A piece of junk!";
        }

        public void buildCar(Car car) {
            car.setSpeed (0.1);
        }
    }
}

汽车类:

public class Cars {
    private List<Car> cars = new List<Car>;

    public void createCar() {
        // There would be logic here to determine what kind
        // We'll make a Corvette just to demonstrate
        Car car = new Car(1998, CarTypes.CORVETTE);
        cars.add(car);
    }
}

Car class:

public class Car {
    private int year;
    private double speed;

    public Car(int year, CarType type) {
        this.year = year;
        type.buildCar(this);  // Sets the speed based on CarType
    }

    public void setSpeed(double speed) {
        this.speed = speed;
    }
}

答案 2 :(得分:2)

你必须在某个地方说出来 - 没有解决这个问题。如果您有更多种类的汽车,则必须在某处指定这些属性。

除非你能识别出相似的汽车系列(例如跑车,小型货车等),否则我看不到子类化在哪里对你有多大帮助。

将所有这些属性集中在一个地方的一种方法是使用工厂方法实例化汽车(它也与制造实体汽车具有良好的对称性)。

将Car构造函数设为私有;有一个CarType的枚举,它是每种类型的属性Map的关键;传递CarType并取回一辆完全初始化的汽车。

public class Car {

   private static final Map<CarType, Map<String, Object>> DEFAULT_ATTRIBUTES;

   private Map<String, Object> attributes; 

   static {
      DEFAULT_ATTRIBUTES = new HashMap<CarType, Map<String, Object>>();
      // Insert values here
   }

   private Car(Map<String, Object> initializers) {  
      this.attributes = new HashMap<String, Object>(initializers);
   }

   public static Car create(CarType type) {
      return new Car(DEFAULT_ATTRIBUTES.get(type));
   }
}

答案 3 :(得分:0)

  

我可以使用子类,也就是说,有一个由Corvette和Clunker扩展的抽象Car类,但后来我遇到了使用Cars对象的问题(因为我不能制作一些可以'实例化。)

这取决于你如何使用以及如何创建Car对象,但无论如何,创建对象应该总是很容易,我使用工厂进行成像就可以了。

您需要扩展Car对象以获得您想要的字段,而在具体的类中,您可以单独分配它们,因此而不是

Car car = new Car(1998, Corvette);
你做了

Car car = new Corvette(1998);

并在你的Corvette课程中:

public Corvette(int year)
{
    speed = 0.9;
}

但请记住,始终确保Car是Corvette,Corvette是Car。