Domanda

Ho pensato a modi per fornire zucchero sintattico per un framework su cui ho lavorato. Voglio occuparmi esclusivamente di oggetti Immitable.

Dire che ho un oggetto immutabile e desidero crearne una versione modificata. A tuo avviso, una classe non istantanea con un singolo metodo statico di fabbrica infrangerebbe i principi di OO?

  

Ad esempio usando una stringa:

public final class LOWERCASE {

    private LOWERCASE() {}

    public static String string( final String STRING ) {

      return STRING.toLowerCase();
    }
}
     

Quindi da questo esempio potrei scrivere:

String lowercaseString = LOWERCASE.string( targetString );
     

Che trovo molto leggibile.

Qualche accantonamento contro tale approccio?

È stato utile?

Soluzione

Non credo sia una buona idea creare una classe per metodo. È possibile invece creare una classe di metodi solo statici, denominata ad esempio StringUtils e implementare i metodi. In questo modo chiameresti:

String lowerCaseString = StringUtils.lowercase (targetString);

Questo ti offrirebbe anche aiuto intellisense durante la digitazione. L'elenco delle lezioni andrà altrimenti troppo grande. Anche per questo semplice esempio, è necessario implementare più di una classe in minuscolo, in modo da poter soddisfare anche le circostanze in cui CulutureInfo deve essere preso in considerazione.

Non penso che questo rompa i principi di OO in alcun modo o che sia un cattivo design. In altre lingue, ad esempio Ruby, potresti aggiungere i tuoi metodi direttamente alla classe String. Metodi che finiscono con! denota che l'oggetto originale è stato modificato. Tutti gli altri metodi restituiscono una copia modificata. Il framework Ruby on Rails aggiunge alcuni metodi alla classe String e si discute se questa sia una buona tecnica o meno. È sicuramente utile però.

Altri suggerimenti

Di solito su oggetti immutabili, avrei un metodo che restituisce una versione modificata dell'oggetto. Quindi, se hai una raccolta immutabile, può avere un metodo sort (), che restituisce una nuova raccolta che viene ordinata. Tuttavia, nel tuo esempio String questo non è possibile, poiché non puoi toccare la classe String.

Il tuo approccio è abbastanza leggibile, e penso che per casi limite come questo, vada benissimo. Per gli oggetti immutabili che scrivi tu stesso, avrei il metodo sull'oggetto stesso.

La serie di Eric Lippert sugli oggetti immutabili in C # è abbastanza bene, comunque.

Secondo me, l'unico punto di una tale classe di fabbrica sarebbe se la classe fornisse diversi tipi di oggetti immutabili.

Esempio:

public final class Lowercase {

  private Lowercase() {}

  public static String string( final String string ) {

   return new String( string.toLowerCase() );
  }

  public static Foo foo( final Foo f ) {
     boolean isLowerCase = true;
     return new Foo(f, isLowerCase );
  }
}

Altrimenti, dovresti implementare il tuo metodo nella stessa classe immutabile come toLowerCase () nella classe String.

Ad ogni modo, non credo che ciò rompa alcun principio OO.

Sono d'accordo con Erik. Gli oggetti immutabili dovrebbero sempre avere un metodo che restituisce una versione modificata dell'oggetto piuttosto che un metodo statico. Ci sono anche esempi nel JDK:

  • String.subString (int)
  • BigDecimal.movePointLeft (int)

Farlo in questo modo ha il vantaggio di non dover passare l'istanza che si desidera modificare come argomento per un metodo. Per classi come String o Integer preferirei utilizzare una classe wrapper. Quindi puoi controllare quando viene creato un tale oggetto (dal costruttore della classe wrapper o da uno dei metodi della classe). Se utilizzi la classe Integer è molto più complicato controllarlo poiché tutti possono crearne un'istanza.

D'altra parte il tuo esempio è considerato in alcune classi di utilità come StringUtils del pacchetto apache commons-lang. Dai un'occhiata a questo perché penso che tu abbia voluto creare qualcosa del genere. (Non reinventare la ruota)

new String () è un odore di codice - è quasi sempre inutile a causa della immutabilità di String . Una volta che un'istanza String ha un valore, quell'istanza non avrà mai un valore diverso.

Nel seguente metodo, new String () è ridondante:

public static String string( final String string ) {
    return new String( string.toLowerCase() );
}

toLowerCase () restituisce una nuova (diversa) istanza String - new String () non fa nulla di utile qui oltre alla causa creazione di un altro oggetto con il valore esatto dell'istanza String restituita da toLowerCase()

Ecco un piccolo script Groovy che mostra il concetto (spero - nota, questo è Java sotto il linguaggio di scripting):

String a = 'CamelCase'
String b = a.toLowerCase()

println "a='${a}'"
println "b='${b}'"

la produzione

a='CamelCase'
b='camelcase'

Nota che a non è cambiato - è immutabile; b è un nuovo valore String .

Lo stesso vale per BigDecimal.movePointLeft () o per qualsiasi altro metodo su BigDecimal che restituisce un BigDecimal : sono nuove istanze , lasciando invariata l'istanza originale.

OK, ora per rispondere alla tua domanda:

Avere una serie di operazioni per stringhe che svolgono uno scopo utile nella tua applicazione è una buona idea. L'utilizzo di una factory probabilmente non è necessario per qualcosa come String , ma potrebbe essere per una diversa classe immutabile che richiede uno sforzo per costruire.

Nel caso in cui non sia possibile estendere la classe base, come String , una classe metodo statico come descritta da @kgiannakakis va bene.

Altrimenti, se "immutabile" class fa parte dell'applicazione, in cui hai accesso alla dichiarazione / definizione della classe, i metodi che restituiscono una nuova istanza, nel modello di BigDecimal , String , ecc. preferibile. Questo è essenzialmente ciò che hanno detto @Erik Hesselink, @ Bruno Conde e @reallyinsane.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top