Questa istanza di mixare FP e OOP è un buon progetto?

0

Ho avuto un pattern di stranezza in un codice che stavo scrivendo. In un progetto con account utente, c'era un sacco di codice necessario per fare cose comuni come la creazione di account, la cancellazione, l'accesso e la disconnessione, l'estensione della sessione di accesso, ecc. Ecc. Ecc. Ora, quando ero prima scrivendo questo, ho appena buttato tutte quelle responsabilità in una classe (che era orribile lo so). Quando è arrivato il momento di ripulire tutto, ho finito con un approccio che sembrava giusto ma anche strano.

Ho impostato una classe AccountAccessor che conteneva funzioni private per ottenere gli oggetti di trasferimento dati associati a un account, quindi ho creato diverse classi di "azione" che ognuna farebbe una cosa con accesso a un account, permettendomi di scrivere:

new AccountPasswordSetter(new AccountAccessor(username)).SetPassword(password);

Questo mi sembrava sbagliato, perché

  1. Le classi di azione erano molto, molto strettamente associate alla classe AccountAccessor - non c'era speranza di testarle
  2. La classe AccountAccessor dovrebbe esporre le sue funzioni di accesso ai dati affinché le classi di azioni possano utilizzarle.
  3. Devo esporre la costruzione delle classi di azioni al codice cliente. Ciò è sembrato problematico perché, in realtà, non c'è alcun motivo ragionevole per cui qualcosa al di fuori del progetto debba preoccuparsi della loro istanziazione.
  4. Ciascuna classe di azioni richiedeva solo una degli oggetti di trasferimento dati forniti da AccountAccessor - ad esempio, una classe di azioni responsabile della registrazione dell'utente non ha bisogno di accedere alla password dell'account.
  5. Scrivere ciò sembra molto verboso e "dentro e fuori".

Quello che ho finito invece è stato che ho reso le classi di azioni tutte in grado di essere costruite solo all'interno del progetto, e invece di aver bisogno di AccountAccessor le ho impostate per accettare semplicemente una funzione che avrebbe preso una connessione al database e restituire l'unico DTO di cui avevano bisogno. Quindi, quando è stata costruita la funzione di accesso all'account, si creerebbe un'intera serie di questi oggetti azione e si passerà a ciascuno di essi uno dei metodi privati per ottenere il DTO. Qualcosa come:

public class AccountAccessor {
    public AccountPasswordSetter PasswordSetter { get; private set; }
    public AccountSessionExtender SessionExtender { get; private set; }
    public string Username { get; private set; }

    public AccountAccessor(string username) {
        Username = username;
        PasswordSetter = new PasswordSetter(getPasswordEntity);
        SessionExtender = new SessionExtender(getSessionEntity);
    }

    private PasswordEntity getPasswordEntity(Connection databaseConnection) { ... }
    private SessionEntity getSessionEntity(Connection databaseConnection) { ... }
}

Ora, il codice cliente può semplicemente chiamare qualcosa del tipo:

new AccountAccessor(username).PasswordSetter.Set(password)'

Le responsabilità sono suddivise e le classi di azioni non devono sapere o preoccuparsi di cosa sia un AccountAccessor. Il codice cliente non deve sapere o preoccuparsi di come vengono impostati i vari oggetti che forniscono funzionalità. AccountAccessor espone solo selettivamente l'accesso alle entità del database. Questo mi è sembrato molto, molto meglio. Quello che mi è piaciuto di più di questo design è stato risolvere molti dei problemi che avevo senza imporre alcun problema di architettura al codice client. Se il codice cliente vuole disconnettere un account ed eliminarlo, ottiene solo un accesso, quindi lo registra ed elimina: non è necessario capire come trasferire correttamente gli oggetti. L'approccio ovvio per il codice client funziona solo .

Ma sembra ancora un po 'off - passando metodi privati come argomenti per gli altri oggetti mi sembra fondamentalmente strano. Volevo sapere di più su che tipo di problemi ha questo, e cosa dovrei tenere a mente quando progetti le cose che si adattano a questo tipo di scenario in futuro.

    
posta Jack 19.09.2014 - 18:21
fonte

2 risposte

4

passing private methods as arguments to other objects comes across as fundamentally strange to me.

Si chiamano funzioni di prima classe. È una tecnica perfettamente valida .

Detto questo, sei sicuro che non sia tutto un po 'troppo ingegnerizzato? Cosa c'è di sbagliato semplicemente facendo qualcosa di simile?

user.SetPassword(newPassword);

Se sei infastidito da un accoppiamento stretto, allora fornisci un sovraccarico per il costruttore che fornisce il meccanismo necessario per impostare una password per l'oggetto User . L'iniezione di dipendenza è anche una tecnica perfettamente valida. Se desideri un ulteriore disaccoppiamento, utilizza un contenitore IoC.

    
risposta data 19.09.2014 - 18:32
fonte
0

Penso che le tue dichiarazioni

new AccountPasswordSetter(new AccountAccessor(username)).SetPassword(password);

e

new AccountAccessor(username).PasswordSetter.Set(password);

non può beneficiare di FP. È bene che tu conosca Scheme, quindi posso usare le terminologie FP per spiegare: chiaramente l'unico scopo delle tue affermazioni sopra è di causare effetto collaterale .

The purpose of an expression is to compute a value, not to cause a side effect. The purpose of a statement is to cause a side effect.

La seguente affermazione dovrebbe essere abbastanza chiara:

user.Password = password;

Questo è OOP di base, ma all'interno del setter puoi usare FP.

public string Password
{
    ...
    set { _password = Encrypt(value); }
}

Questa volta, Encrypt(value) è un'espressione, non un'istruzione. Il metodo può essere testato indipendentemente se è static . Finché l'input è lo stesso, l'output è sempre lo stesso, indipendentemente dal fatto che ci sia o meno un database. Quindi puoi suddividere l'operazione complessa in espressioni piccole e testabili.

    
risposta data 27.09.2014 - 07:22
fonte

Leggi altre domande sui tag