Si sta trasformando un metodo in una classe per usarlo in molte cattive pratiche? [chiuso]

3

Diciamo che ho un metodo sort all'interno della mia classe e un'altra classe che non ha alcuna relazione con quella classe ha bisogno dello stesso metodo. Invece di scrivere quel metodo due volte e di non ripetere il principio di OOP, decido di creare una classe come se fosse un'invenzione per risolvere quel metodo chiamandolo Sorter. Ho visto questo fatto prima e di solito contengono più di uno dei metodi più comuni.

Si tratta di una progettazione procedurale perché manterrebbe una serie di metodi per tutte le classi, ma non è necessaria in un modello della mia soluzione o è OOP perché è un algoritmo / evento che sto modellando?

    
posta user275564 03.02.2016 - 20:14
fonte

2 risposte

1

Poiché la domanda è taggata con Java , mi concentrerò su di essa nella risposta:

Il motivo principale per queste classi era che Java non aveva alcun metodo semplice per passare una funzione come parametro (ad esempio C (++) ha dei puntatori alle funzioni). Un buon esempio per quando questo è davvero fastidioso è un metodo di ordinamento generico. L'unico modo per passare un metodo di confronto personalizzato al metodo di ordinamento era creare una classe con una funzione di confronto, come

interface ISorter {
    boolean compare(Object o1, Object o2);
}

class Sorter implements ISorter {
    boolean compare(Object o1, Object o2) { /* do the comparison */ }
}

[...]

Vector<Object> sort(Vector<Object> objs, ISorter sortBy) { 
     // do the sorting using the sortBy object
}

// actual call
Vector<Object> sorted = sort(objs, new Sorter());

Questo è, ovviamente, noioso come dovevi definire un'interfaccia così come una classe che implementa quell'interfaccia (che chiamerò oggetto funzione ). Se desideri semplicemente modificare il criterio di ordinamento da "<=" a ">=", questo ha comportato un enorme sovraccarico.

Ma ora lambda al salvataggio : queste cose ti permettono di definire le funzioni degli oggetti "al volo". Ciò significa che puoi scrivere codice simile a

sort(objsToSort, (Object o1, Object o2) -> o1 <= o2))
sort(objsToSort, (Object o1, Object o2) -> o1.length() >= o2.length()))
sort(objsToSort, (Object o1, Object o2) -> abritraryFunctionReturningComparison(o1,o2))

In questi casi è del tutto ovvio ciò che stai facendo, non è necessario utilizzare classi per indovinare quale comparazione verrà utilizzata per l'ordinamento.

Ma questo non significa che avere una classe che è una "falsa invenzione" è sempre sbagliato. Se si desidera specificare alcune funzionalità complesse, inserirle in una classe separata potrebbe essere più appropriato. Ciò è particolarmente vero se implementa un intero algoritmo che utilizza variabili temporanee.

Riguardo questo argomento dovresti, come già menzionato, leggere questo post del blog fantastico

    
risposta data 03.02.2016 - 22:12
fonte
5

Gli oggetti sono modelli. Non devono corrispondere agli oggetti del mondo reale. A volte le azioni devono essere modellate.

Prendi, ad esempio, lo scenario tipico del conto bancario, che viene utilizzato in molti corsi introduttivi di OO. Il design che viene insegnato assomiglia un po 'a questo:

class BankAccount {
  Money balance;

  void deposit(Money amount) {
    balance += amount;
  }

  bool withdraw(Money amount) {
    if (balance < amount) { return false; }
    balance -= amount;
    return true;
  }

  bool transfer(Money amount, BankAccount target) {
    if (balance < amount) { return false; }
    withdraw(amount);
    target.deposit(amount);
    return true;
  }
}

Quindi, come puoi vedere, balance è data e transfer è un'operazione. Ha senso, vero?

Ma ci sono un paio di problemi. Ad esempio, se ho due conti bancari e voglio trasferire fondi da uno all'altro, ma entrambi hanno un metodo transfer , perché è il metodo da chiamare con transfer nell'account sorgente? Perché non quello nell'account di destinazione?

Possiamo modellarlo in modo diverso? Sì possiamo! In effetti, come viene fatto il banking "nel mondo reale"? Non ci sono soldi nel tuo account. E quando trasferisci fondi, non vengono prelevati soldi dal tuo account e inseriti in un altro account. Viene invece creato un fascicolo di transazione , che elenca l'account di origine, l'account di destinazione e l'importo. E alla fine della giornata, le polizze di transazione vengono tutte sommate e quindi sappiamo quanti soldi ci sono in ogni account.

Nota come abbiamo quasi doppiato il design ora: la quantità non è più dati, è un'operazione, e il trasferimento non è più un'operazione, sono dati:

class Transaction {
  BankAccount source;
  BankAccount target;
  Money amount;
}

class BankAccount {
  Money balance() {
    // find all transaction slips which have 'this' as either source or target
    // add all the amounts which have 'this' as target
    // subtract all the amounts which have 'this' as source
    return result;
  }

  void deposit(Money amount) {
    TransactionLog.append(new Transaction(CASH, this, amount));
  }

  void withdraw(Money amount) {
    TransactionLog.append(new Transaction(this, CASH, amount));
  }
}

Questo modello presenta alcuni vantaggi. Nota: il fatto che sia più vicino a come funziona il banking "nel mondo reale" in realtà non è necessariamente un vantaggio. Dopotutto, stiamo costruendo un sistema software, non una banca, non siamo vincolati dalle realtà fisiche, quindi il sistema non deve rispecchiare il mondo reale.

Per uno: non c'è (quasi) nessuna mutazione e nessun effetto collaterale. Tutti gli oggetti sono immutabili e tutti i metodi sono referenzialmente trasparenti. (Sto sorvolando su come potrebbe essere implementato TransactionLog.append , però). Aggiungere asincronia, concorrenza e parallelismo a questo sistema sarà più facile rispetto al primo. Il TransactionLog lascia una traccia di controllo di tutte le transazioni. Questo è generalmente importante per il settore bancario. (Ad esempio, potresti aggiungere un ID utente dell'utente che ha avviato la transazione agli oggetti Transaction .)

Non c'è anche asimmetria in transfer . Non è più nemmeno un metodo del conto, ora sarà un metodo del sistema bancario.

Come puoi vedere, possiamo arrivare a due progetti completamente opposti per lo stesso identico problema. Nessuno di questi è giusto o sbagliato. Sono, come ho detto, modelli e se sono o meno "buoni" modelli dipendono dal contesto in cui verranno utilizzati e dalle proprietà importanti che dovrebbero modellare.

All'inizio della veeeeery di un corso OO, a volte viene insegnato il metodo di progettazione semplicemente scrivendo il caso d'uso e sottolineando tutti i verbi e i nomi. I nomi diventano oggetti, i verbi diventano metodi. E l'istruttore insegnerà spesso questo come se fosse un metodo di progettazione infallibile. Ma considera questo: in inglese, puoi pronunciare un verbo e anche il nome dei verbi è possibile. Quindi, dipende davvero da come scrivi il caso d'uso.

    
risposta data 03.02.2016 - 21:04
fonte

Leggi altre domande sui tag