2015-04-07 17 views
5

Sono in difficoltà a causa di questo:Come gestire l'accoppiamento temporale?

Le mie classi hanno alcuni metodi che hanno accoppiamento temporale. Questo è, alcuni metodi MethodA deve essere invocato prima per "inizializzare" i dati che il metodo B deve funzionare correttamente.

solito io faccio l'accoppiamento temporale esplicito facendo passare la dipendenza incriminato per "MethodB"come argomento, come in questo frammento:

private class SomeClass 
{ 
    private string field; 
    private int count; 

    public SomeClass() 
    { 
     MethodA(); 
     MethodB(field); 
    } 

    private void MethodA() 
    { 
     field = "Something"; 
    } 

    private void MethodB(string str) 
    { 
     count = str.Length; 
    } 
} 

Anche se rende le cose esplicita Sento che sto facendo Qualcosa non va. Finisco per avere un metodo che non usa affatto i campi (metodi statici!), Quindi la classe inizia a sembrare meno coesa.

È questo il modo migliore per farlo? (perdendo la coesione passando argomenti)

MODIFICA: per quanto riguarda alcune risposte che suggeriscono di utilizzare il campo come parametro nel costruttore o di utilizzare il modello di generatore per evitare stati non validi: Non riesco a farlo perché nel mio caso sto costruendo un parser. MethodA legge l'input e imposta lo stato in base ad esso (leggendo i caratteri da un file) e quindi viene richiamato MethodB. Devono essere invocati nell'ordine corretto. Questo è il vero problema: uno dovrebbe essere invocato prima dell'altro.

+0

Sei preoccupato per la coesione nei tuoi metodi privati ​​o è un esempio forzato? – neontapir

+0

@neontapir: la coesione più alta non suona come qualcosa di cui dovrebbe essere preoccupato. –

+0

Questo articolo può essere utile: [Accoppiamento temporaneo tra chiamate di metodi] (http://www.yegor256.com/2015/12/08/temporal-coupling-between-method-calls.html) – yegor256

risposta

1

Si può solo rimuovere il parametro da MethodB e utilizzare il field, in questo modo non si perde coesione

private class SomeClass 
{ 
    private string field; 
    private int count; 

    public SomeClass() 
    { 
     MethodA(); 
     MethodB(); 
    } 

    private void MethodA() 
    { 
     field = "Something"; 
    } 

    private void MethodB() 
    { 
     count = field.Length; 
    } 
} 

Note:

1) Il modo in cui si descrive il problema sembra come il modello Metodo modello, è necessario have a look here.

2) Static methods non appartengono che classe

+0

Questo è tecnicamente sbagliato. I metodi statici possono appartenere solo a una classe. Non appartengono a * istanza *. –

+0

A quale metodo statico ci si riferisce, @ThorstenDittmar? Tutti questi sembrano metodi di istanza per me. – neontapir

+0

@ThorstenDittmar Hai ragione, i metodi statici devono essere inseriti in una classe, ma * non quella classe *. – adricadar

0

Non so qual è il tuo obiettivo preciso, ma perché non mettere il parametro nel costruttore della classe:

private class SomeClass 
{ 
    private string _field; 
    private int _count; 

    public SomeClass(string field) 
    { 
     _field = field; 
     _count = field.Length; 
    } 

} 

Ora si avrà qualcosa di simile

SomeClass sc = new SomeClass("Something");//or whatever you want for field. 
3

Credo che è necessario disporre di una sorta di inizializzazione del complesso, in cui alcuni para i metri devono essere specificati prima di inizializzare effettivamente l'oggetto e si desidera un controllo migliore su ciò che l'utente della classe sta facendo per evitare stati non validi. Un modello noto per risolvere tale situazione è il cosiddetto "Builder Pattern", molto frequentemente utilizzato in OOP. Non voglio indicare un particolare articolo, ti troverai un sacco di esempi semplicemente usando la parola chiave "builder pattern". Per essere completa, l'idea generale è di creare una sequenza fluente di metodo che specifichi i valori dei campi interni e delegare un metodo finale "Build" per creare un'istanza di un oggetto di lavoro e convalidare i parametri passati.

+0

OK, vedo il tuo punto, ma il vero problema è che sto costruendo un parser che legge i caratteri in modo sequenziale, quindi MethodA legge dall'input, imposta alcuni campi su uno stato valido e il MethodB viene invocato. Non puoi anticipare l'input. Builder è ottimo per alcune situazioni, ma per quanto riguarda questo caso? – SuperJMN

+1

Perché stai leggendo il file nella classe Parser? Non capisco qualcosa o il tuo approccio è in conflitto con i principi SOLID? – mybirthname

3

perfetto API di risolvere questo genere di cose sulle interfacce pubbliche, non esponendo metodi dipendenti nell'oggetto "costruttore" fino a quando appropriato:

SomeClass someInstance = SomeClassBuilder(x=> { 
    x.MethodA().MethodB("somevalue"); 
}); 

Ciò richiede molto più plumbling perché è necessario l'oggetto costruttore, così come componenti del builder come un oggetto che viene restituito da MethodA che espone MethodB. In questo modo l'unico modo per chiamare MethodB è chiamare prima MethodA.

Non ti sto incoraggiando a seguire questo approccio. Probabilmente è eccessivo per molti scenari, ma è importante essere a conoscenza di questa opzione nel caso in cui si verifichi uno scenario in cui è appropriato.

+0

In realtà penso che questo sia un approccio praticabile a questo problema. – neontapir

3

Se si segue il modello di dominio anemico, è possibile rompere la classe e renderla 2 classi più piccole. Ti rendi conto del cattivo design perché la tua classe attuale viola lo SRP, in breve ha 2 responsabilità: 1 per gestire il processo di input, 1 per elaborare il risultato di input.

scomposizione modo che ClassA gestirà il risultato dell'inserimento e ritorno, poi ClassB prenderà il risultato da ClassA come parametro, allora elaborarlo. es:

public class ClassA 
{ 
    public string MethodA() 
    { 
     // read the input 
     return "Something"; // or return the input 
    } 
} 

public class ClassB 
{ 
    private int count; 
    public void MethodB(string str) 
    { 
     count = str.Length; 
    } 
} 

Se si trova l'uso di entrambi classe è fastidioso, utilizzare un altro aggregate service per questo. es:

public class ClassC 
{ 
    public ClassA ClassA = new ClassA(); 
    public ClassB ClassB = new ClassB(); 
    public void Execute(){ 
     string result = ClassA.MethodA(); 
     ClassB.MethodB(result); 
    } 
} 
+0

Soluzione perfetta – Mateusz