2015-04-20 12 views
5

Sono nuovo al concetto di programmazione orientata agli oggetti (in java) e ho spesso incontrano il seguente problema di progettazione:Best Practice: variabili di istanza di riempimento nel tempo

ho spesso creano classi con variabili di istanza che non sono noti al momento dell'inizializzazione. Queste variabili sono riempite nel tempo. Questo non è un problema reale poiché queste variabili sono nulle fino a quando non vengono riempite, pertanto la mia domanda riguarda maggiormente le migliori pratiche in tali situazioni.

Lasciatemi spiegare con un esempio. Ho una classe Car. Ogni Car ha un color, vMax, weight, horsepower ecc

Quando si inizializza l'Car solo che è color, weight e horsepower sono noti. ->Car(color, weight, horsepower)

Il vMax può ora essere calcolato (diciamo: weight/horsepower). Ciò che mi confonde è che lo Car, dopo l'inizializzazione è "incompleto", il che significa che lo vMax sarà riempito solo nel tempo.

Ho trovato questo piuttosto brutto, e, naturalmente, l'esempio di macchina è semplificato. Spesso ho classi con 10+ proprietà in cui alcune sono calcolate nel tempo, che in seguito vengono utilizzate nel codice per calcolare ancora più proprietà di quell'oggetto. Diventa quindi difficile sapere quali variabili sono già state riempite in un determinato punto e quali no.

Mi chiedevo solo se questo è "normale" e il modo in cui funziona OOP, o se tali situazioni dovrebbero essere evitate. Se sì, sarei felice per alcuni suggerimenti sul design.

Michael

+4

mi piacerebbe suggeriamo di esaminare il pattern fluente del builder, usando proprietà nullable (quindi i valori primitivi predefiniti non vengono confusi con i valori non assegnati) e convalidando tutte le proprietà necessarie in uno o più metodi privati, richiamati quando necessario. – Mena

+1

Sembra che 'vMax' dovrebbe essere un metodo piuttosto che una variabile ... – beerbajay

+0

@beerbajay - Preferirei una variabile * transient * invece di un metodo per' vMax' :) – TheLostMind

risposta

2

Come si definisce vMax dovrebbe essere un altro method di un attributo della classe:

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

Penso che questa sia la strada da percorrere, a almeno per le proprietà che possono essere calcolate dalle proprietà già esistenti. Non è necessario avere una variabile di istanza effettiva per un oggetto per avere una proprietà, un metodo che lo calcola è anche buono. –

+0

Ho pensato alla stessa identica soluzione. Questo è anche in linea con il principio "Tell Do not Ask". Cioè, invece del codice client che chiede 'double vMax = car.getWeight()/car.getHorsePower', il client può solo dire' car.getVMax() ' – CKing

0

Sembra che si potrebbe impostare Vmax in costruttore come sono passati il ​​peso dei parametri e la potenza dentro.

0

Se si hanno a che fare con sempre più proprietà, suggerirei di andare con Properties design pattern and prototype based Java Programming. Altrimenti, vorrei progettare come di seguito, calcolare vMax nel costruttore stesso, in modo che non è necessario convalidare per null ogni volta che si accede.

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 
} 

Se non è una volta, probabilmente un metodo separato è l'approccio migliore. Per lo più, getVMax tenterà sempre di calcolare il vMax come indicato di seguito.

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

Si può semplicemente calcolare il valore vMax nel costruttore, in questo modo:

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 
} 

E assicuratevi di controllare che horsePower valore non è pari a zero.

1

Tutto dipende da ciò che ti serve.

La prima cosa che mi viene in mente è quello di utilizzare builder (soprattutto tenuto conto che lei ha citato "le classi con oltre 10 proprietà")

L'idea principale del costruttore è non si crea un oggetto, fino a quando non ne conosci/ne calcoli tutte le proprietà.

Torniamo al tuo esempio di auto.

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

Poi si può costruire la vostra macchina come questa

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

Questo approccio ha i seguenti vantaggi:

  • oggetto Car è costruito quando tutti i campi obbligatori sono impostati. Se è necessaria una convalida aggiuntiva , è possibile aggiungerla al metodo createCar()
  • È possibile passare oggetti Builder tra diversi metodi per compilare i campi.
  • Ogni volta che si dispone di un oggetto Car, si è sicuri che è valido (non può essere un'istanza altrimenti)
  • l'oggetto Car è thread-safe :)

Speranza che aiuta.

+0

Grazie! (anche @Mena per il commento nel post originale) per avermi indirizzato al modello di builder. Penso che sia quello che ho intenzione di dare un'occhiata! Ovviamente il builder sarà "incompleto" fino a quando tutti i campi non saranno riempiti, ma dal momento che si tratta di un costruttore che è esattamente ciò a cui è destinato, quindi renderà il mio codice molto più interessante, e non ci saranno oggetti (finali) inclusivi intorno . Grazie –

+0

Non penso che un costruttore ti aiuti. Perché devi ancora specificare * tutte * le proprietà di cui hai bisogno prima di creare effettivamente l'auto. Da ciò che descrivi, alcune proprietà vengono impostate solo dopo aver effettivamente utilizzato l'oggetto. –

+0

Inoltre, avere un metodo 'setVMax()' mi sembra pericoloso. Poiché vMax è calcolato in termini di peso e potenza, è possibile creare un oggetto incoerente, in cui vMax è diverso dal peso/potenza. –

0

A seguito di Maks Builder, un po 'più fluida ...:

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

    } 

} 

Poi basta:

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

Se si utilizza Eclipse, vedere Bob the Builder: https://code.google.com/a/eclipselabs.org/p/bob-the-builder/

Nota: ho spostato il metodo di convalida nella classe Car, poiché credo che questo sia una versione più sicura dei thread. Bob lo genererà sul Builder.

0

Lo schema del builder si adatta a questo scenario ma, poiché si parla di OOP, è perfettamente opportuno aggiungere più proprietà in una classe in un secondo momento come & quando richiesto. Supponiamo che per proprietà come vMax=weight/horsepower sia possibile scrivere un metodo & rendere vMax disponibile per il codice esterno alla classe tramite il metodo getter.

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

È possibile creare inizialmente l'oggetto di classe con alcune proprietà richieste inizializzandole nel costruttore.

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

È possibile scrivere costruttori sovraccaricati ma il modello di builder sarebbe una soluzione molto più pulita. Troppi costruttori sovraccaricati non avranno un bell'aspetto.

costruttore sovraccaricato dire si desidera una proprietà length macchina per creare un oggetto in un secondo tempo

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

Nella classe client creare l'oggetto secondo il vostro requisito

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