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

发布于 2024-12-05 15:14:07 字数 1927 浏览 0 评论 0原文

场景

我正在用 Java 编写一个涉及汽车的程序。

注意:我(尽我所能)简化了这个场景,使其更通用、更容易理解。我实际上并没有使用汽车。

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

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

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

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

这是棘手的部分...汽车必须有一种类型(比如说 Corvette 或 Clunker)。 Corvette 的速度0.9,Clunker 的速度0.1。如果不指定Car应该是什么类型的汽车,就永远无法实例化它。现在,为了创建一辆汽车,我们需要:

Car car = new Car(1998, Corvette);

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

问题

我的实际情况涉及到更多种类的汽车,每种汽车除了速度之外还有几个特定的​​属性(也许还有颜色的字段) 、numDoorsfuelTankSize)。由于汽车种类繁多(每种汽车都有自己的特定属性),代码变得比我想象的更加复杂。

可能的解决方案

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

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

    • 填充每辆车的速度字段
    • Cars 类创建 Car 对象
    • 等等

如何提供帮助

我正在寻找一种解决方案,允许单个 Cars 类包含每个 <代码>汽车对象。我想要不同的收藏(例如CorvettesClunkers)。我还在寻找一种解决方案,允许根据单个汽车类型的属性创建 Car 对象...如前所述,创建一个新的 Car Corvette速度0.9。不应该有其他方法来指定汽车的速度

在这种情况下有最佳实践吗?我举的例子够清楚了吗?

谢谢。

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

The Scenario

I'm making a program in Java that involves cars.

NOTE: I've simplified this scenario (to the best of my ability) to make it both more general and easier to understand. I'm not actually working with cars.

I've created a Cars class, which is a collection of Car objects.

The Car object has a speed (double) and a year (int). The constructor takes the year as a parameter, for example:

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

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

Here's the tricky part... A car must have a kind (let's say Corvette or Clunker). A Corvette will have a speed of 0.9 and a Clunker will have a speed of 0.1. A Car can never be instantiated without specifying what kind of car it should be. So now, to create a car, we have:

Car car = new Car(1998, Corvette);

The Corvette we've just created will be a Car object with a speed of 0.9.

The Problem

My actual situation involves many more kinds of cars, and each car has several specific attributes besides speed (maybe there are also fields for color, numDoors and fuelTankSize). With so many kinds of cars (each with their own specific attributes), the code is becoming more complex than I'd like.

Possible Solutions

  1. I could work with sub classes, that is, have an abstract Car class that's extended by Corvette and Clunker, but then I have the problem of using a Cars object (because I can't make a collection of something that can't be instantiated). See EDIT below.

  2. Using an enum (such as CarKind) seemingly requires several messy switch statements:

    • to populate the speed field of each car
    • to create Car objects from the Cars class
    • etc.

How You Can Help

I'm looking for a solution that allows a single Cars class to contain every Car object. I don't want different collections (like Corvettes, Clunkers). I'm also looking for a solution that allows the creation of Car objects based on the attributes of an individual car kind... as previously mentioned, creating a new Car of kind Corvette would result in a speed of 0.9. There should be no other way to specify a car's speed.

Is there a best practice in this situation? Have I made the example clear enough?

Thanks.

EDIT: The reason I don't want a collection of abstract Car objects is because the point of the Cars collection is to create and manipulate Car objects, regardless of their kinds. Car being abstract seems to complicate this. If you think this is the best solution, please answer accordingly.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

半边脸i 2024-12-12 15:14:07

我正在寻找一种解决方案,允许单个 Cars 类包含
每个汽车对象。我不想要不同的收藏(比如克尔维特,
旧车)。我也在寻找一种允许创建的解决方案
基于单个汽车类型属性的汽车对象...如
之前提到过,创造一款类似 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:使用 Factory。

Factory 也是一个不错的解决方案,而且可能更简单。您要做的就是拥有一个 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));

所以这样做的好处是您现在不必担心实例化 Car。它全部由 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,您会注意到这就是它们处理诸如布局之类的一些事情的方式(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(例如,从数据存储中提取汽车属性)。

I'm looking for a solution that allows a single Cars class to contain
every Car object. I don't want different collections (like Corvettes,
Clunkers). I'm also looking for a solution that allows the creation of
Car objects based on the attributes of an individual car kind... as
previously mentioned, creating a new Car of kind Corvette would result
in a speed of 0.9. There should be no other way to specify a car's
speed.

Oh boy oh boy there are so many ways to deal with this that we could go on all day! I will do a brain dump, and hopefully it will not be too much for you to deal with.


solution 1: use Strategy.

A strategy is basically a way to separate heavy substitutable logic from another class. In this case, every car needs to be created differently. A strategy is PERFECT for this.

Sorry if I mix in some C# by accident... been a long time since I javaed.

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";
   }
}

Now, you can pass a strategy in with your car constructor.

public class Car{
   // Variables ...

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

So, what you get now is so awesome!

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

And this will do exactly what you want.

However, you get a coupling between the strategy and the Car.


solution 2: use Factory.

Factory is an okay solution for this as well, and is probably easier. What you do is have a CarFactory, with multiple factory methods for creating each type of car.

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;
   }
}

Usage:

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

So the good thing about this is you don't have to worry about instantiating Car now. its all handled by CarFactory, decoupling your "instantiation logic" from your code. However, you still need to know which car you want to build and call that method accordingly, which is still a small coupling.


solution 3: strategy factory!

So, if we wanted to get rid of that last bit of couplings, lets combine the two together!

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

Now you have a Strategy for building cars, a Factory that builds them for you, and a Car with no extra couplings from your original. Wonderful, isn't it?

If you have worked with Swing, you will notice that this is how they handle a few things like the Layouts (GridBagLayout, GridLayout are all strategies). There's also a BorderFactory as well.


Improvement

Abstract Strategy

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";
   }
}

Using this gives you the flexibility to create AbstractStrategies on the fly (say, pulling car properties from a data store).

安穩 2024-12-12 15:14:07

我使用了 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);
        }
    }
}

Cars 类:

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 类:

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;
    }
}

I used the method described in Daryl Teo's answer, but I modified it a little bit to include an enum (since there are a finite number of individual cars). Because his answer was so instrumental to the development of my final solution, I selected it as the best answer.

The CarCreationStrategy interface:

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);
        }
    }
}

The Cars class:

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);
    }
}

The 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;
    }
}
dawn曙光 2024-12-12 15:14:07

你必须在某个地方说出来——这是无法回避的。如果您有更多种类的汽车,则必须在某处指定这些属性。

我不认为子类化对您有多大帮助,除非您可以识别相似的汽车系列(例如跑车、小型货车等)。

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

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

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));
   }
}

You have to say it someplace - there's no getting around that. If you have many more kinds of Cars, you'll have to specify those attributes somewhere.

I don't see where subclassing helps you much, unless you can identify families of cars that are similar (e.g. sports cars, mini-vans, etc.).

One way to centralize all those attributes in one place is to instantiate Car using a factory method (it has a nice symmetry with manufacturing of physical cars, too).

Make the Car constructor private; have an enumeration of CarType that is the key into a Map of attributes for each type; pass in the CarType and get back a Car, fully initialized.

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));
   }
}
我做我的改变 2024-12-12 15:14:07

我可以使用子类,也就是说,有一个由 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,克尔维特是一辆汽车。

I could work with sub classes, that is, have an abstract Car class that's extended by Corvette and Clunker, but then I have the problem of using a Cars object (because I can't make a collection of something that can't be instantiated).

It depends on how you use and how you create your Car object, but anyway, creating your object should always been easy, I'd imaging using a factory will do.

You will need to extend your Car object to have fields you wanted in general, and in concrete class, you can assign them individually, so instead of

Car car = new Car(1998, Corvette);

you do

Car car = new Corvette(1998);

and in your Corvette class:

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

But keep in mind always make sure Car is a Corvette, and Corvette is a Car.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文