2015-04-20 9 views
5

Я новичок в концепции объектно-ориентированного программирования (в Java), и я часто сталкиваются со следующей проблемой: дизайнЛучшие практики: -переменные заполняющие с течением времени

Я часто создавать классы с переменными экземпляра, которые не известны в момент инициализации. Эти переменные заполняются с течением времени. Это не настоящая проблема, так как эти переменные равны нулю до тех пор, пока они не будут заполнены, поэтому мой вопрос связан скорее с лучшей практикой в ​​таких ситуациях.

Позвольте мне объяснить это на примере. У меня есть класс Car. Каждый Car имеет color, vMax, weight, horsepower и т.д.

При инициализации Car только это color, weight и horsepower известны. ->Car(color, weight, horsepower)

Теперь можно рассчитать vMax (скажем: weight/horsepower). Меня смущает то, что Car после инициализации является «неполным», что означает, что vMax будет заполняться только со временем.

Я нашел это довольно уродливым, и, конечно, пример автомобиля упрощен. У меня часто есть классы с 10+ свойствами, где некоторые из них вычисляются с течением времени, которые сами позже в коде используются для вычисления еще большего количества свойств этого объекта. Затем становится трудно узнать, какие переменные уже заполнены в определенной точке, а какие нет.

Я просто задавался вопросом, является ли это «нормальным» и как работает ООП, или следует избегать таких ситуаций. Если да, я был бы рад некоторым советам по дизайну.

Майкл

+4

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

+1

Кажется, что 'vMax' должен быть методом, а не переменной ... – beerbajay

+0

@beerbajay - Я бы предпочел переменную * переходный * вместо метода для' vMax' :) – TheLostMind

ответ

2

Как вы определяете vMax должно быть более method, чем атрибут класса:

class Car { 
    private String color; 
    private double weight; 
    private double horsePower; 

    // constructor for Car 
    public Car (String color, double weight, double horsePower) { 
     this.color = color; 
     this.weight= weight; 
     this.horsePower= horsePower; 
    } 

    // usually I would create this as one-line-method... but to clarify: 
    public double getVMax() { 
     double vMax = weight/horsePower; // calculate vMax!!! 
     return vMax; 
    } 
} 
+1

Я думаю, что это путь, на наименее для свойств, которые можно вычислить из уже существующих свойств. Нет необходимости иметь фактическую переменную экземпляра для объекта, обладающего свойством, метод, который его вычисляет, также хорош. –

+0

Я думал о том же решении. Это также соответствует принципу 'Tell Do not Ask'. То есть вместо кода клиента, запрашивающего 'double vMax = car.getWeight()/car.getHorsePower', клиент может просто сказать' car.getVMax() ' – CKing

0

Это звучит, как вы могли бы установить Vmax в конструкторе, как вес параметров и лошадиные силы передаются внутрь.

0

Если у вас есть все больше и больше свойств, я бы предложил пойти с Properties design pattern and prototype based Java Programming. Else, я бы разработал, как показано ниже, вычисляет vMax в самом конструкторе, поэтому каждый раз, когда вы обращаетесь к нему, не требуется проверять значение для null.

class Car { 
    private String color; 
    private double weight; 
    private double horsePower; 

    Car(String color, double weight, double horsePower){ 
    // other fields 
    calculateVMax(); 
    } 

    private void calculateVMax() { 
     double vMax = weight/horsepower; 
     setVMax(vMax); // initialize vMax 
    } 
    // setters and getters for vMax, color, weight and horse power 
} 

Если это не один раз, то, возможно, отдельный метод - лучший подход. В основном, getVMax всегда будет пытаться вычислить vMax, как указано ниже.

public double getVMax(){ 
    return (double) (weight/horsePower); 
} 
0

Вы можете просто вычислить значение vMax в конструкторе, например:

class Car { 
    private String color; 
    private double weight; 
    private double horsePower; 
    private double vMax; 

    public Car(){ 
     super(); 
    } 

    public Car(String color, double weight, double horsePower){ 
     this.color = color; 
     this.weight = weight; 
     this.horsePower = horsePower; 
     if(horsePower!=0) { 
      this.vMax = weight/horsePower; 
     } else { 
      this.vMax = 0; 
     } 
    } 

    // setters and getters 
} 

И убедитесь, что вы убедитесь, что horsePower значение не равно нулю.

1

Все зависит от того, что вам нужно.

Самое первое, что приходит на ум, чтобы использовать построитель (особенно принимать во внимание, что вы упомянули «классы с 10+ свойствами»)

Основная идея строителя это не создать объект, пока вы не узнаете/не вычислите все его свойства.

Вернемся к примеру вашего автомобиля.

class Car 
{ 
    final int weight; 
    final int horsepower; 
    final int vMax; 
    final String color; 

    private Car(int weight, int horsepower, int vMax, String color) 
    { 
     this.weight = assetNotNull(weight); 
     this.horsepower = assetNotNull(horsepower); 
     this.vMax = assetNotNull(vMax); 
     this.color = assetNotNull(color); 
    } 

    //..... other car-related methods 

    /** Car builder */ 
    public static class Builder 
    { 
     int weight; 
     int horsepower; 
     int vMax; 
     String color; 

     public Builder setWeight(int weight) 
     { 
      this.weight = weight; 
      return this; 
     } 

     public Builder setHorsepower(int horsepower) 
     { 
      this.horsepower = horsepower; 
      return this; 
     } 

     public Builder setvMax(int vMax) 
     { 
      this.vMax = vMax; 
      return this; 
     } 

     public Builder setColor(String color) 
     { 
      this.color = color; 
      return this; 
     } 

     public Car createCar() 
     { 
      return new Car(weight, horsepower, vMax, color) 
     } 
    } 
} 

Тогда вы можете создать свой автомобиль, как этот

Car.Builder builder = new Car.Builder(); 
Car car = builder.setColor("blue") 
      .setHorsepower(10) 
      .setvMax(100) 
      .setWeight(1000) 
      .createCar(); 

Этот подход имеет следующие преимущества:

  • объект Автомобиль построен, когда все необходимые поля установлены. Если вам нужна дополнительная проверка , вы можете добавить ее в метод createCar()
  • Вы можете передавать объекты Builder между различными методами для заполнения полей.
  • Всякий раз, когда у вас есть объект Car, вы уверены, что она действует (он не может быть инстанцирован иначе)
  • ваш объект Car потокобезопасен :)

Надежда, что помогает.

+0

Спасибо! (также @Mena для комментария в исходном сообщении) для указания на шаблон строителя. Я думаю, это то, на что я собираюсь взглянуть! Строитель тоже будет «неполным», пока все поля не будут заполнены, но поскольку это застройщик, это именно то, для чего он предназначен, поэтому мой код будет намного больше ¨clearer, и не будет объектов с неполной (конечной) , Спасибо –

+0

Я не думаю, что строитель вам поможет. Потому что вам еще нужно указать * all * свойства, которые вам нужны, прежде чем создавать автомобиль. Из того, что вы описали, некоторые свойства задаются через некоторое время после фактического использования объекта. –

+0

Кроме того, мне кажется, что метод 'setVMax()' мне кажется опасным. Поскольку vMax рассчитывается с точки зрения веса и мощности, вы можете собрать несогласованный объект, в котором vMax отличается от веса/лошадиной силы. –

0

Следуя от Maks Builder, чуть более свободно ...:

import org.apache.commons.lang3.Validate; 

class Car { 
    private String color; 
    private double weight; 
    private double horsePower; 
    private double vMax; 

    // setters and getters 

    public String getColor() { 
     return color; 
    } 

    public double getWeight() { 
     return weight; 
    } 

    public double getHorsePower() { 
     return horsePower; 
    } 

    public double getvMax() { 
     return vMax; 
    } 

    private void validate() { 
     Validate.isTrue(!StringUtils.isBlank(color), "color may not be blank"); 
     Validate.isTrue(weight > 0L, "weight should be set"); 
     Validate.isTrue(horsePower > 0L, "horsePower should be set"); 

     if(horsePower!=0) { 
      this.vMax = weight/horsePower; 
     } else { 
      this.vMax=0; 
     }   
    } 

    private Car(Builder builder) { 
     this.color = builder.color; 
     this.weight = builder.weight; 
     this.horsePower = builder.horsePower;   
    } 

    public static class Builder { 

     private String color; 
     private double weight; 
     private double horsePower; 

     public Builder withColor(String color) { 
      this.color = color; 
      return this; 
     } 

     public Builder withWeight(double weight) { 
      this.weight = weight; 
      return this; 
     } 

     public Builder withHorsePower(double horsePower) { 
      this.horsePower = horsePower; 
      return this; 
     }    

     public Car build() { 
      Car newCar = new Car(this); 
      newCar.validate(); 
      return newCar; 
     } 

    } 

} 

Тогда просто:

Car car = new Car.Builder() 
      .withColor("blue") 
      .withHorsepower(10) 
      .setWeight(1000) 
      .build(); 

Если вы используете затмение, см Бобу Builder: https://code.google.com/a/eclipselabs.org/p/bob-the-builder/

Обратите внимание: я переместил метод проверки в класс Car, так как я считаю, что это делает более надежную версию. Боб будет генерировать его на Builder.

0

В этом случае шаблон строителя будет подходящим, но, поскольку вы говорите о ООП, совершенно нормально добавлять дополнительные свойства в класс в более поздний момент времени, как & при необходимости. Скажем, для свойств, таких как vMax=weight/horsepower, вы можете написать способ & сделать vMax доступным для кода вне класса методом getter.

public void setVMax(int weight, int horsepower){ 
vMax=weight/horsepower; 
} 

Первоначально вы можете создать свой объект класса с определенными требуемыми свойствами, инициализируя их в конструкторе.

public Car (String color, double weight, double horsePower) { 
     this.color = color; 
     this.weight= weight; 
     this.horsePower= horsePower; 
    } 

Вы можете написать перегруженные конструкторы, но шаблон строителя будет намного более чистым решением. Слишком много перегруженных конструкторов не будет выглядеть хорошо.

Перегруженный конструктор, вы хотите, свойство длина автомобиля, чтобы создать объект в более поздний момент времени

public Car (String color, double weight, double horsePower, int length) { 
     this.color = color; 
     this.weight= weight; 
     this.horsePower= horsePower; 
     this.length=length; 
     } 

В классе клиента создать объект согласно вашему требованию

new Car("Red", 56.78, 789); 
new Car("Blue", 67.98, 567, 45);