Utilizza il metodo costruttore o setter?

16

Sto lavorando su un codice UI in cui ho una classe Action , qualcosa del genere -

public class MyAction extends Action {
    public MyAction() {
        setText("My Action Text");
        setToolTip("My Action Tool tip");
        setImage("Some Image");
    }
}

Quando questa classe Action è stata creata, si presupponeva che la classe Action non fosse personalizzabile (in un certo senso - il suo testo, la descrizione del comando o l'immagine non saranno cambiati in nessun punto del codice). Ora, abbiamo bisogno di cambiare il testo dell'azione in qualche punto del codice. Quindi, ho suggerito al mio collaboratore di rimuovere il testo di azione codificato dal costruttore e accettarlo come argomento, in modo che tutti siano obbligati a passare il testo dell'azione. Qualcosa come questo codice qui sotto -

public class MyAction extends Action {
    public MyAction(String actionText) {
        setText(actionText);
        setTooltip("My Action tool tip"); 
        setImage("My Image"); 
    }
}

Tuttavia, pensa che dal momento che il metodo setText() appartiene alla classe base, può essere utilizzato in modo flessibile per passare il testo dell'azione ovunque venga creata l'istanza dell'azione. In questo modo, non è necessario modificare la classe MyAction esistente. Quindi il suo codice sarebbe simile a questo.

MyAction action = new MyAction(); //this creates action instance with the hardcoded text
action.setText("User required new action text"); //overwrite the existing text.

Non sono sicuro che sia un modo corretto per affrontare il problema. Penso che nel caso sopra menzionato l'utente cambierà comunque il testo, quindi perché non forzarlo mentre costruisce l'azione? L'unico vantaggio che vedo con il codice originale è che l'utente può creare una classe Action senza pensare troppo all'impostazione del testo.

    
posta zswap 29.06.2012 - 07:18
fonte

9 risposte

15

The only benefit I see with the original code is that user can create Action class without much thinking about setting text.

Questo in realtà non è un vantaggio, per la maggior parte degli scopi è uno svantaggio e nei casi rimanenti lo definirei un pareggio. Cosa succede se qualcuno dimentica di chiamare setText () dopo la costruzione? Cosa succede se questo è il caso in qualche caso insolito, forse un gestore di errori? Se vuoi veramente forzare la definizione del testo, devi forzarlo alla compilazione, dato che solo gli errori in fase di compilazione sono in realtà fatali . Tutto ciò che accade in fase di esecuzione dipende da quel particolare percorso di codice che viene eseguito.

Vedo due percorsi chiari in avanti:

  1. Utilizza un parametro costruttore, come suggerisci. Se lo desideri, puoi passare null o una stringa vuota, ma il fatto che non stai assegnando un testo è esplicito piuttosto che implicito. È facile vedere l'esistenza di un parametro null e vedere che probabilmente ci sono stati alcuni pensati, ma non è così facile vedere la mancanza di una chiamata al metodo e determinare se la mancanza di tale era intenzionale o no. Per un caso semplice come questo, questo è probabilmente l'approccio che prendo.
  2. Utilizza uno schema factory / builder. Questo può essere eccessivo per uno scenario così semplice, ma in un caso più generale è altamente flessibile in quanto consente di impostare qualsiasi numero di parametri e controllare le precondizioni prima o durante l'istanza dell'oggetto (se la costruzione dell'oggetto è un'operazione di grandi dimensioni e / o la classe può essere usata in più di un modo, questo può essere un vantaggio enorme ). In particolare in Java è anche un linguaggio comune, e seguire schemi consolidati nel linguaggio e nel framework che stai utilizzando è molto raramente una cosa negativa.
risposta data 29.06.2012 - 10:01
fonte
10

Il sovraccarico del costruttore sarebbe una soluzione semplice e diretta qui:

public class MyAction extends Action {
    public MyAction(String actionText) {
        setText(actionText);
        setTooltip("My Action tool tip"); 
        setImage("My Image"); 
    }
    public MyAction() {
        this("My Action Text");
    }
}

È meglio che chiamare .setText più tardi, perché in questo modo non è necessario sovrascrivere nulla, actionText può essere la cosa desiderata fin dall'inizio.

Man mano che il tuo codice si evolve e avrai bisogno di una flessibilità ancora maggiore (che sicuramente accadrà), trarrai vantaggio dal modello factory / builder suggerito da un'altra risposta.

    
risposta data 29.06.2012 - 10:16
fonte
6

Aggiungi un metodo "setText" scorrevole:

public class MyAction ... {
  ...
  public MyAction setText(String text) { ... ; return this; }
}

MyAction a = new MyAction().setText("xxx");

Cosa potrebbe essere più chiaro di quello? Se decidi di aggiungere un'altra proprietà personalizzabile, nessun problema.

    
risposta data 29.06.2012 - 19:19
fonte
1

Proprio come kevin cline ha detto nella sua risposta, penso che la strada da percorrere sia creare un API fluente . Vorrei solo aggiungere che l'API fluente funziona meglio quando hai più di una proprietà che puoi usare.

Renderà il tuo codice più leggibile, e dal mio punto di vista più facile e, aham , "sexy" da scrivere.

Nel tuo caso sarebbe andato così (scusami per qualsiasi refuso, è passato un anno da quando ho scritto il mio ultimo programma java):

 public class MyAction extends Action {
    private String _text     = "";
    private String _tooltip  = "";
    private String _imageUrl = "";

    public MyAction()
    {
       // nothing to do here.
    }

    public MyAction text(string value)
    {
       this._text = value;
       return this;
    }

    public MyAction tooltip(string value)
    {
       this._tooltip = value;
       return this;
    }

    public MyAction image(string value)
    {
       this._imageUrl = value;
       return this;
    }
}

E l'utilizzo sarebbe come questo:

MyAction action = new MyAction()
    .text("My Action Text")
    .tooltip("My Action Tool tip")
    .image("Some Image");
    
risposta data 17.08.2012 - 03:26
fonte
1

Il consiglio di usare costruttori o costruttori va bene in generale, ma, nella mia esperienza, manca alcuni punti chiave per le azioni, che

  1. Forse è necessario essere internazionalizzati
  2. È probabile che il marketing cambi all'ultimo minuto.

Suggerisco caldamente che il nome, la descrizione, l'icona, ecc ... vengano letti da un file di proprietà, XML, ecc. Ad esempio, per l'azione Apri file, è possibile passare in una Proprietà e cercare

File.open.name=Open
File.open.tooltip=Open a file
File.open.icon=somedir/open.jpg

Questo è un formato abbastanza facile da tradurre in francese, per provare una nuova icona migliore, ecc. Senza orario del programmatore o una ricompilazione.

Questo è solo uno schema approssimativo, molto è lasciato al lettore ... Cerca altri esempi di internazionalizzazione.

    
risposta data 07.08.2014 - 06:46
fonte
0

È inutile chiamare setText (actionText) o setTooltip ("My tool tool tip") all'interno del costruttore; è più semplice (e ottieni più prestazioni) se si inizializza semplicemente il campo corrispondente direttamente:

    public MyAction(String actionText) {
        this.actionText = actionText;
    }

Se modifichi actionText durante la vita dell'oggetto corrispondente di MyAction, devi inserire un metodo setter; se non si inizializza il campo solo nel costruttore senza fornire un metodo setter.

Dal momento che tooltip e image sono costanti, trattali come costanti; avere campi:

private (or even public) final static String TOOLTIP = "My Action Tooltip";

In realtà, quando si progettano oggetti comuni (non i bean o gli oggetti che rappresentano rigorosamente le strutture dei dati) è una cattiva idea fornire setter e getter, dato che si tratta di un tipo di incapsulamento di break.

    
risposta data 29.06.2012 - 08:12
fonte
0

Penso che questo sia vero se stiamo per creare una classe di azione generica (come l'aggiornamento, che viene utilizzato per aggiornare Dipendente, Dipartimento ...). Dipende tutto dallo scenario. Se una classe specifica di azione (come aggiornamento dipendente) (utilizzata in molti posti nell'applicazione - Aggiornamento dipendente) viene creata con l'intensione di mantenere lo stesso testo, suggerimento e immagine in ogni punto dell'applicazione (per il punto di vista della coerenza). In questo modo è possibile eseguire l'hardcoding per testo, suggerimento e immagine per fornire il testo, il suggerimento e l'immagine predefiniti. Ancora per dare più flessibilità, per personalizzare questi, dovrebbe avere metodi setter corrispondenti. Tenendo presente solo il 10% dei posti dobbiamo cambiarlo. L'esecuzione di un testo di azione ogni volta dall'utente può causare un testo diverso ogni volta per la stessa azione. Come "Update Emp", "Update Employee", "Change Employee" o "Edit Employee". Non sono sicuro, ma penso che questo possa creare confusione per l'utente che questo è qualcosa di diverso.

    
risposta data 29.06.2012 - 12:06
fonte
0

Pensa a come verranno utilizzate le istanze e utilizzerai una soluzione che guidi, o addirittura imponga, agli utenti di utilizzare tali istanze nel modo giusto, o almeno migliore. Un programmatore che usa questa classe avrà molte altre cose di cui preoccuparsi e pensare. Questa classe non dovrebbe aggiungersi alla lista.

Ad esempio, se la classe MyAction dovrebbe essere immutabile dopo la costruzione (e probabilmente un'altra inizializzazione), non dovrebbe avere un metodo setter. Se la maggior parte delle volte utilizza il "My Action Text" predefinito, dovrebbe esserci un costruttore senza parametri, oltre a un costruttore che consente un testo opzionale. Ora l'utente non ha bisogno di pensare di usare correttamente la classe il 90% delle volte. Se l'utente di solito dovrebbe per riflettere sul testo, salta il costruttore senza parametri. Ora l'utente è costretto a pensare quando è necessario e non può trascurare un passaggio necessario.

Se un'istanza MyAction deve essere modificabile dopo la costruzione completa, è necessario un setter per il testo. Si è tentati di ignorare l'impostazione del valore nel costruttore (principio DRY - "Non ripetere te stesso") e, se il valore predefinito è in genere abbastanza buono, lo farei. Ma se non lo è, richiedere il testo nel costruttore costringe l'utente a pensare quando dovrebbe.

Nota che questi utenti non sono stupidi . Hanno solo troppi problemi reali di cui preoccuparsi. Pensando alla "interfaccia" della tua classe, puoi evitare che diventi un vero problema anche se non necessario.

    
risposta data 29.06.2012 - 18:52
fonte
0

Nella seguente soluzione proposta, la superclasse è astratta e ha tutti e tre i membri impostati su un valore predefinito.

La sottoclasse ha costruttori diversi in modo che il programmatore possa istanziarla.

Se viene utilizzato il primo costruttore, tutti i membri avranno i valori predefiniti.

Se viene utilizzato il secondo costruttore, si fornisce un valore iniziale al membro actionText lasciando gli altri due membri con il valore predefinito ...

Se si utilizza il terzo costruttore, si crea un'istanza con un nuovo valore per actionText e toolTip, lasciando imageURl con il valore predefinito ...

E così via.

public abstract class Action {
    protected String text = "Default action text";
    protected String toolTip = "Default action tool tip";
    protected String imageURl = "http://myserver.com/images/default.png";

    .... rest of code, I guess setters and getters
}

public class MyAction extends Action {


    public MyAction() {

    }

    public MyAction(String actionText) {
        setText(actionText);
    }

    public MyAction(String actionText, String toolTip_) {
        setText(actionText);
        setToolTip(toolTip_);   
    }

    public MyAction(String actionText, String toolTip_; String imageURL_) {
        setText(actionText);
        setToolTip(toolTip_);
        setImageURL(imageURL_);
    }


}
    
risposta data 16.08.2012 - 16:13
fonte

Leggi altre domande sui tag