抽象类的集合(或类似的东西……)
情景
我正在制作一个涉及汽车的Java程序。
注意:我已经简化了这种情况(尽我所能),使其更通用,更容易理解。 我实际上并不是在和汽车一起工作。
我创建了一个Cars
类,它是Car
对象的集合。
Car
对象具有speed
(double)和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将是一个speed
为0.9
的Car
对象。
问题
我的实际情况涉及更多种类的汽车,每辆汽车除了speed
之外还有几个特定属性(也许还有color
, numDoors
和fuelTankSize
)。 有这么多种车(每种都有自己的特定属性),代码变得比我想要的更复杂。
可能的解决方案
-
我可以使用子类,也就是说,有一个由
Corvette
和Clunker
扩展的抽象Car
类,但后来我Clunker
了使用Cars
对象的问题(因为我不能创建一些无法实例化的东西的集合)。 见下面的编辑。 -
使用枚举(如
CarKind
)似乎需要几个凌乱的switch语句:- 填充每辆车的
speed
场 - 从
Cars
类创建Car
对象 - 等等
- 填充每辆车的
你可以如何帮助
我正在寻找一个允许单个Cars
类包含每个Car
对象的解决方案。 我不想要不同的系列(如Corvettes
, Clunkers
)。 我也正在寻找一种解决方案,允许根据单个汽车类型的属性创建Car
对象……如前所述,创建一辆新型Corvette
Car
将导致0.9
的speed
。 应该没有其他方法来指定汽车的speed
。
在这种情况下有最好的做法吗? 我让这个例子清楚了吗?
谢谢。
编辑:我不想要抽象Car
对象集合的原因是因为Cars
集合的要点是创建和操作Car
对象,而不管它们的种类。 抽象的Car
似乎使这变得复杂。 如果您认为这是最佳解决方案,请相应地回答。
我正在寻找一个允许单个Cars类包含每个Car对象的解决方案。 我不想要不同的系列(如Corvettes,Clunkers)。 我也正在寻找一种解决方案,允许根据单个汽车类型的属性创建Car对象……如前所述,创建一辆新型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 cars = new List (); 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 cars = new List (); 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 cars = new List (); 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"; } }
使用此function,您可以灵活地动态创建AbstractStrategies(例如,从数据存储中提取汽车属性)。
我使用了Daryl Teo的答案中描述的方法,但我修改了一点以包含枚举(因为有一定数量的单个车)。 因为他的答案对我最终解决方案的开发非常有帮助,所以我选择它作为最佳答案。
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 cars = new List ; 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); } }
汽车类:
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; } }
你必须在某个地方说出来 – 没有解决这个问题。 如果你有更多种类的汽车,你必须在某处指定这些属性。
除非你能识别出相似的汽车系列(例如跑车,小型货车等),否则我看不到子类化在哪里对你有多大帮助。
在一个地方集中所有这些属性的一种方法是使用工厂方法实例化汽车(它也与制造实体汽车具有良好的对称性)。
使Car构造函数私有化; 有一个CarType的枚举,它是每种类型的属性Map的关键; 传递CarType并获得一辆Car,完全初始化。
public class Car { private static final Map> DEFAULT_ATTRIBUTES; private Map attributes; static { DEFAULT_ATTRIBUTES = new HashMap>(); // Insert values here } private Car(Map initializers) { this.attributes = new HashMap(initializers); } public static Car create(CarType type) { return new Car(DEFAULT_ATTRIBUTES.get(type)); } }
我可以使用子类,也就是说,有一个由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。