Коллекция абстрактного класса (или что-то в этом роде ...) - PullRequest
7 голосов
/ 21 сентября 2011

Сценарий

Я делаю программу на Java, которая включает автомобили.

ПРИМЕЧАНИЕ. Я упростил этот сценарий (в меру своих возможностей), чтобы сделать его более общим и понятным. На самом деле я не работаю с машинами.

Я создал класс Cars, представляющий собой коллекцию объектов Car.

Объект Car имеет speed (double) и year (int). Конструктор берет год как параметр, например:

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

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

Вот сложная часть ... У машины должен быть вид (скажем, Корвет или Кланкер). Корвет будет иметь speed 1022 *, а Кланкер будет иметь speed 1024 *. A Car никогда не может быть создан без указания типа машины. Итак, теперь, чтобы создать автомобиль, мы имеем:

Car car = new Car(1998, Corvette);

Корвет, который мы только что создали, будет Car объектом с speed из 0.9.

Проблема

В моей реальной ситуации много других типов автомобилей, и у каждого автомобиля есть несколько специфических атрибутов, кроме speed (возможно, есть также поля для color, numDoors и fuelTankSize). С очень многими видами автомобилей (каждый со своими специфическими атрибутами) код становится более сложным, чем мне хотелось бы.

Возможные решения

  1. Я мог бы работать с подклассами, то есть иметь абстрактный класс Car, который расширен на Corvette и Clunker, но тогда у меня возникла проблема с использованием объекта Cars (потому что я не может создать коллекцию чего-то, что не может быть создано). См. РЕДАКТИРОВАТЬ ниже.

  2. Использование перечисления (такого как CarKind), по-видимому, требует нескольких операторов беспорядочного переключения:

    • для заполнения поля speed каждого автомобиля
    • для создания Car объектов из Cars класса
    • и т.д.

Как вы можете помочь

Я ищу решение, позволяющее одному классу Cars содержать каждый Car объект. Я не хочу разные коллекции (например, Corvettes, Clunkers). Я также ищу решение, которое позволяет создавать Car объекты на основе атрибутов отдельного типа автомобиля ... как упоминалось ранее, создание нового Car вида Corvette приведет к speed из 0.9. Не должно быть другого способа указать автомобиль speed.

Есть ли лучшая практика в этой ситуации? Достаточно ли ясен пример?

Спасибо.

РЕДАКТИРОВАТЬ: причина, по которой я не хочу коллекцию абстрактных Car объектов, заключается в том, что цель коллекции Cars заключается в создании и манипулировании Car объектами, независимо от их вида. Car абстрактность, кажется, усложняет это. Если вы считаете, что это лучшее решение, ответьте соответственно.

Ответы [ 4 ]

10 голосов
/ 21 сентября 2011

Я ищу решение, которое позволяет одному классу Cars содержать каждый объект Car.Я не хочу разные коллекции (например, корветы, кланкеры).Я также ищу решение, которое позволяет создавать объекты 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<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));

Так что хорошо, что вы этого не делаететеперь надо беспокоиться о создании экземпляра 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 на лету (скажем, потянувсвойства автомобиля из хранилища данных).

3 голосов
/ 21 сентября 2011

Я использовал метод, описанный в Ответ Дэрила Тео , но я немного изменил его, добавив enum (поскольку существует конечное число отдельных автомобилей). Поскольку его ответ сыграл важную роль в разработке моего окончательного решения, я выбрал его как лучший ответ.

Интерфейс 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);
    }
}

Класс автомобиля:

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 голосов
/ 21 сентября 2011

Вы должны сказать это где-нибудь - с этим не обойтись. Если у вас есть еще много видов автомобилей, вам нужно будет где-то указать эти атрибуты.

Я не понимаю, где подклассификация вам очень помогает, если только вы не можете определить семейства похожих автомобилей (например, спортивные автомобили, минивэны и т. Д.).

Один из способов централизовать все эти атрибуты в одном месте - создать экземпляр Car с использованием заводского метода (он также имеет хорошую симметрию с производством физических автомобилей).

Сделать конструктор Car приватным; иметь перечисление CarType, которое является ключом в карте атрибутов для каждого типа; передать 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));
   }
}
0 голосов
/ 21 сентября 2011

Я мог бы работать с подклассами, то есть иметь абстрактный класс Car, расширенный Corvette и Clunker, но тогда у меня возникла проблема с использованием объекта Cars (потому что я не могу сделать коллекцию чего-либоэто не может быть создано).

Это зависит от того, как вы используете и как вы создаете свой объект Car, но в любом случае, создание вашего объекта всегда должно быть легким, я бы сделал снимок с использованием фабрики.do.

Вам нужно будет расширить свой объект Car, чтобы иметь поля, которые вы хотели в общем, и в конкретном классе вы можете назначать их индивидуально, поэтому вместо

Car car = new Car(1998, Corvette);

вы делаете

Car car = new Corvette(1998);

и в вашем классе Corvette:

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

Но всегда помните, что Car - это Corvette, а Corvette - это Car.

...