2009-05-18 15 views
11

Quali sono i vantaggi e quando è opportuno utilizzare un costruttore statico?Perché utilizzare un metodo Create anziché utilizzare "nuovo"?

public class MyClass 
{ 
    protected MyClass() 
    { 
    } 

    public static MyClass Create() 
    { 
     return new MyClass(); 
    } 
} 

e quindi creando un'istanza della classe tramite

MyClass myClass = MyClass.Create(); 

piuttosto che semplicemente avere un costruttore pubblico e creare oggetti utilizzando

MyClass myClass = new MyClass(); 

posso vedere il primo approccio è utile se il metodo Create restituisce un'istanza di un'interfaccia implementata dalla classe ... forza i chiamanti a creare istanze dell'interfaccia anziché il tipo specifico.

risposta

15

Questo è il modello di fabbrica e potrebbe essere spesso utilizzato per produrre una sottoclasse, ma consentire ai parametri del metodo statico di determinare quale.

Può anche essere utilizzato se non è sempre richiesto un nuovo oggetto, ad esempio in un'implementazione di pooling.

Può anche essere utilizzato se tutte le istanze dell'oggetto devono essere memorizzate nella cache o altrimenti registrate al momento della creazione. Ciò garantisce che il cliente non dimentichi di farlo.

E, naturalmente, è parte integrante del modello Singleton.

+1

Vorrei aggiungere a questa risposta la risposta di Robert Paulson in merito a "fare qualcosa di più della semplice creazione" in quanto sembrerebbe completare questa risposta o almeno estendere la "parte della risposta in cache o altrimenti registrata". –

1

Questo schema viene in genere utilizzato nello Singleton pattern. È utile quando si ha una classe o una facciata per una classe che dovrebbe essere istanziata una sola volta durante la vita di un programma, tipicamente a causa delle risorse che l'oggetto utilizzerà.

How to in C#

+2

Il modello Singleton restituisce un riferimento allo stesso oggetto, mentre il modello di fabbrica crea più istanze che possono condividere i dati. –

+1

Come menzionato BillyONeal, questo non è il pattern di Singleton; Singleton costringe a creare solo un'istanza di una classe. Un ottimo esempio di quando si potrebbe voler utilizzare il pattern Singleton è per le classi di impostazioni in cui solo uno dovrebbe mai esistere. –

+0

Mentre entrambi siete corretti e il suo codice non trasmette necessariamente un singletone, ricordate che la sua domanda originale ruota attorno all'uso * della costruzione statica * che sia la fabbrica che il singleton usano. –

1

Ci sono vari motivi si potrebbe fare questo, ad esempio, per tornare sempre un'istanza da un pool oggetto fisso (tra cui un oggetto Singleton), o come si dice, per restituire istanze di un'interfaccia.

Ma se non si dispone di una ragione chiara per farlo, non farlo.

2

Potrebbe essere utile nei casi in cui più istanze di classe condividono lo stesso blocco di dati inizializzati: questo è il modello di fabbrica OOP.

Un esempio sarebbe una classe che si connette a un database: è richiesta solo una connessione e può essere condivisa da tutte le istanze della classe.

http://en.wikipedia.org/wiki/Factory_pattern

Billy3

+0

Questo non richiede un metodo di fabbrica. Può essere realizzato con una semplice variabile di connessione statica. –

+0

Vero. Ma non è così che viene scritto il codice dell'OP. –

1

Questo approccio è solo comunemente usato per Singletons, tuttavia questo approccio è ortodosso. Non usare fino a quando non capisci cosa sta cercando di fare.

+0

Grazie Jon, so come, quando e perché usare Singletons ma sono abbastanza sicuro di aver visto esempi di codice che usano ciò che non sono Singletons ... Potrebbero ovviamente essere sbagliati ed è per questo che ho fatto la domanda :-) –

+0

Non usare cosa? Singletons o il modello creativo della costruzione statica. Sono d'accordo con il primo (evita i singleton) ma altrimenti non sarei d'accordo. Il modello builder/factory è abbastanza standard e l'OP sta chiedendo perché, per capire l'uso del pattern. –

1

Questo è un buon modo per forzare MyClass a essere definitivo, se si utilizza il costruttore privato. In questo modo non è possibile creare sottoclassi, perché il loro costruttore non può accedere al costruttore di super.

Un altro vantaggio è quando si dispone di un parametro per il costruttore.Quindi è possibile assegnare un nome alla creazione con emulare argomenti con nome:

public static MyClass createWithDays(int days) { 
... 
} 

Ora confrontate new MyClass(5)-MyClass.createWithDays(5)

8

Questo non è un Singleton ... Creare restituirebbe la stessa istanza ogni volta che se lo fosse.

Questo è un modello di fabbrica. Che normalmente farlo con interfacce non classi, quindi devo allungare qui, ma un esempio forzato sarebbe:

public static MyClass Create() 
{ 
    if(OS == Windows) 
     return new MyDerivedClassForWindows(); 
    else if(OS == Linux) 
     return new MyDerivedClassForLinux(); 
    etc.... 
} 
+0

Vorresti restituire MyClass o IMyClass ... l'ipotesi di MyClass non è un'interfaccia ma IMyClass è (voglio dire, è solo una convenzione, ma tu vedi il mio punto). –

+0

Vorrei restituire IMyClass. Probabilmente i Mac non vorranno derivare da MyClass (solo per il gusto di essere diversi), ma dovranno almeno implementare IMyClass se vogliono giocare nella mia sandbox. – ratchetr

1

Oltre a quando è un singoletto, avente un "metodo factory statico" come MyClass.Create può essere utile se la persona che implementa MyClass.Create potrebbe voler restituire una sottoclasse di MyClass ... ad esempio durante il test potrebbe restituire un'istanza della sottoclasse MyClassWithExtraTestingFunctionality ... oppure, a seconda dell'opzione di configurazione, un'istanza della sottoclasse MyClassBeingRemotedOverTheNetwork.

0

Questo è anche comunemente usato quando si desidera che il framework, la classe o qualche altra configurazione determini DOVE l'oggetto verrà effettivamente creato. Quando si utilizza il nuovo approccio del costruttore, viene creato localmente. Se si utilizza questo approccio, è possibile che venga creato in remoto tramite una chiamata di servizio e restituito, ecc. Questo è l'approccio adottato da Rocky Lhotka nello CSLA framework.

1

Molte delle altre risposte hanno riguardato occasioni specifiche in cui questa idea può essere utile. L'idea di incapsulare la creazione di istanze è utilizzata in molti framework IoC come Spring, Castle e così via. L'idea è che centralizzando la creazione dell'istanza, è possibile personalizzare la configurazione dell'applicazione per utilizzare diversi set di oggetti. Un esempio comune è la registrazione. Modificando la configurazione, è possibile comunicare alla fabbrica del logger di produrre diverse istanze delle classi di registrazione in base alla situazione.

Se applicata, questa idea può portare a sistemi molto sciolti, che possono essere facilmente modificati dopo l'implementazione o anche in fase di esecuzione. Tuttavia, la complessità aumenta con un altro livello di riferimento indiretto.

3

Un'altra ragione, dimostrata da Guid.NewGuid() metodo statico, è se il tipo è uno struct. In questo caso, il CLR richiede che il costruttore parametrico predefinito crei una nuova istanza con tutti i campi inizializzati su valori predefiniti. Nel caso di Guid, questo significa:

Guid g = new Guid();

sarebbe Guid.Empty. Ovviamente vuoi la possibilità di creare un nuovo Guid che è randomizzato, quindi il metodo Guid.NewGuid() è un modo per farlo.

+0

La classe Guid è piuttosto scadente in termini di denominazione: la nuova NewGuid() non è ovviamente l'unico modo per creare un nuovo Guid (casuale) in contrasto con i costruttori che creano Guid solo dai dati esistenti. La tua risposta osserva la realtà, ma non risponde alla domanda perché. Nonostante la scarsa nomenclatura nella classe Guid, è necessario riflettere sul perché Guid.NewGuid() sia diverso dal nuovo Guid(). –

1

un costruttore statico è anche un buon modo per dare i nomi più esplicite al costruttore. Può anche essere utile per il parametro predefinito

ex: Myclass.create(); intead di nuovo Myclass (defaultParam1, defaultParam2 ...)

Joshua Bloch ne parla in effective Java (punto 1)

4

L'esempio particolare che hai postato non sarebbe davvero avere alcun vantaggio rispetto all'uso di un normale costruttore.Ci sono due comuni modelli, tuttavia, che utilizzano un metodo come questo per produrre un oggetto:

Singleton pattern

il pattern Singleton può essere utilizzato quando si vuole evitare che più istanze di un oggetto da essere creato ma ancora voler sfruttare gli aspetti orientati agli oggetti di una classe (ad es. campi, proprietà, metodi senza parametri). Esempio:

public class MySingletonClass 
{ 
    private MySingletonClass currentInstance = null; 

    public MySingletonClass CreateInstance() 
    { 
     if (currentInstance == null) 
     { 
       currentInstance = new MySingletonClass(); 
     } 
     else 
     { 
       return currentInstance; 
     } 
    } 
} 

fabbrica modello

Il modello di fabbrica è una grande opportunità di astrarre la creazione di classi concrete; per esempio, supponiamo per un momento che tu abbia del codice XML e, a seconda del nodo (NodeA, NodeB o NodeC) che vedi nell'XML, hai una classe diversa che la elabora. Esempio:

public class MyXmlProcessorFactory 
{ 
    public static IMyXmlProcessor GetMyXmlProcessor(XmlDocument document) 
    { 
     XmlNode rootNode = document.DocumentElement; 

     if (rootNode.SelectSingleNode("NodeA") != null) 
     { 
       return new NodeAMyXmlProcessor(); 
     } 
     else if (rootNode.SelectSingleNode("NodeB") != null) 
     { 
       return new NodeBMyXmlProcessor(); 
     } 
     else if (rootNode.SelectSingleNode("NodeC") != null) 
     { 
       return new NodeCMyXmlProcessor(); 
     } 
     else 
     { 
       return new DefaultMyXmlProcessor(); 
     } 
    } 
} 
3

Sei corretto. Questo non è uno schema di Singleton. È un modello di fabbrica.

Uso di Create() al posto di nuovo dà permette di dare nomi significativi ai metodi modo anche l'utente più chiarezza

Prendete questo esempio:

public class User 
{ 
    public int id; 
    public string userName; 
    public string password; 
    public string role; 

    private User() { } 

    public static User CreateByCredentials(string uid, string pwd) 
    { 
     User user = new User(); 
     user.userName = uid; 
     user.password = pwd; 
     return user; 
    } 

    public static User CreateById(int id) 
    { 
     User user = new User(); 
     user.id = id; 
     return user; 
    } 

} 

Ci sono due vantaggi di questo approccio : (. Questo può o non può essere utile in tutti i casi)

  1. possiamo restituire un oggetto nullo, che è impossibile da fare con un costruttore
  2. Se ci sono molti modi diversi per creare un oggetto, ti dà la possibilità di fornire nomi di funzioni più significativi. Nell'esempio si ha User.CreateByCredentials (nome utente stringa, password stringa) & User.CreateById (int id). È possibile ottenere la stessa funzionalità con il sovraccarico del costruttore, ma raramente con la stessa chiarezza.
0

Anche questo potrebbe essere utilizzato in caso di caricamento pigro. Dove vuoi che l'oggetto sia in giro per qualche motivo, ma non vuoi fare tutto il lavoro pesante coinvolto nel costruttore reale a meno che non sia necessario. Quindi crei un metodo separato Crea e chiamalo quando sei pronto per il sollevamento pesante.

anche @Rashmi Pandit ha scritto:

public static User CreateByCredentials(string uid, string pwd) 
    { 
     .... 
    } 

    public static User CreateById(int id) 
    { 
    ... 
    } 

Per la vostra risposta, non puoi semplicemente usare costruttori di overload con diverse firme del metodo, invece di creare due diversi metodi di creare?

+0

Il caricamento lento non è influenzato dal modello creativo. Dalla vista di leggibilità del codice, @ il codice di Rashmi è auto-documentante, dove i nuovi costruttori User (string, string) o new User (int) non sono così intuitivi. –

0

Si noti che è possibile utilizzare solo le inizializzazioni del costruttore quando si utilizza l'approccio del costruttore.

public void Example() 
    { 
     var person = new Person 
         { 
          GivenName = "John", 
          FamilyName = "Smith", 
          Address = new Address 
              { 
               Street = "Wallace", 
               Number = 12 
              }, 
          PhoneNumbers = new List<PhoneNumber> 
               { 
                new PhoneNumber 
                 { 
                  Number = 1234234, 
                  AreaCode = 02 
                 }, 
                new PhoneNumber 
                 { 
                  Number = 1234234, AreaCode = 02 
                 } 
               } 
         }; 
    } 

    internal class PhoneNumber 
    { 
     public int AreaCode { get; set; } 

     public int Number { get; set; } 
    } 

    internal class Address 
    { 
     public int Number { get; set; } 

     public string Street { get; set; } 
    } 

    internal class Person 
    { 
     public Address Address { get; set; } 

     public string GivenName { get; set; } 

     public string FamilyName { get; set; } 

     public List<PhoneNumber> PhoneNumbers { get; set; } 
    } 
} 
Problemi correlati