Domanda

Credo in OO, ma non al punto in cui i progetti / le implementazioni inappropriati dovrebbero essere usati solo per essere "quotati OO" &

.

Quindi, come gestire l'architettura a strati Serlvet / EJB / DataContainer:

  • I servlet ricevono richieste e chiamano un "livello aziendale" (ad es. bean di sessione)
  • Il livello aziendale individua i DataContainer dal database e li manipola per implementare la logica aziendale
  • DataContainers non contiene codice reale, basta ottenere / impostare corrispondente al database.

Questo approccio ha un fascino; i DataContainer sono chiari su ciò che fanno ed è molto facile sapere da dove provengono i dati.

Oltre a non essere OO, questo porta a classi Business Layer poco chiare che possono essere difficili da nominare e difficili da organizzare.

Anche se stessimo cercando di essere più " OO " (ad esempio inserendo alcuni di questi metodi nei DataConatiners), alcune di queste operazioni operano su più di una serie di dati.

Come evitare che il tuo livello aziendale diventi procedurale in modo confuso, ma senza inquinare i tuoi DataContainer con la logica aziendale?

Esempio

class UserServlet {
  handleRequest() {
    String id = request.get("id");
    String name = request.get("name");
    if (UserBizLayer.updateUserName(id,name))
      response.setStatus(OK);
    else
      response.setStatus(BAD_REQUEST);
  }
}

class UseBizLayer {
    updateUserName(String id, String name) {
        long key = toLong(id);
        user = userDAO.find(key);
        if user == null
            return false;
        if (!validateUserName(name))
            return false;
        user.setName(name);
        userDAO.update(user);
        return true;
    }

    validateUserName(String name) {
        // do some validations and return
    }
}

class User {
    long key;
    String name;
    String email;

    // imagine getters/setters here
}
  • Non vogliamo validateUserName sull'utente, poiché opera solo con un nome; Immagino che potrebbe andare in un'altra classe, ma poi abbiamo un'altra procedurale "uti" digitare class
  • Non desideriamo metodi di persistenza sull'Utente, poiché esiste un valore nel disaccoppiare le strutture di dati dalla loro strategia di persistenza
  • Non vogliamo la logica aziendale nel nostro Servlet, poiché potrebbe essere necessario riutilizzare tale logica altrove
  • Non vogliamo la nostra logica aziendale nel nostro Utente, poiché ciò si avvicina troppo alla classe Utente, rendendo difficile il riutilizzo della logica aziendale e abbinando l'utente alla sua strategia di persistenza

Mi rendo conto che questo esempio non è poi così male, ma immagino 10 oggetti DataContainer e 20 oggetti BizLayer con diversi metodi ciascuno. Immagina che alcune di queste operazioni non siano "centrate" su un particolare contenitore di dati.

Come possiamo evitare che ciò diventi un pasticcio procedurale?

È stato utile?

Soluzione

Quindi affronterò i miei pensieri su questo in alcuni punti elenco:

  1. Sembra che in un sistema Java EE ad un certo punto tu abbia a che fare con l'impianto idraulico di Java EE, l'impianto idraulico non beneficia sempre dei concetti di OO, ma sicuramente può farlo con un po 'di creatività e lavoro. Ad esempio, potresti trarre vantaggio da elementi come AbstractFactory, ecc. Per contribuire a rendere più comune possibile questa infrastruttura.
  2. Molto di ciò che stai esaminando è discusso nell'eccellente libro di Eric Evans chiamato Domain Driven Design. Consiglio vivamente che lo guardi mentre affronta il problema di esprimere la conoscenza del dominio e di gestire l'infrastruttura tecnica per supportarlo.
  3. Avendo letto e GROKD parte del DDD, avrei incapsulato la mia infrastruttura tecnica in repository. I repository sarebbero tutti scritti per utilizzare una strategia di persistenza basata sui bean di sessione. Scriveresti un'implementazione predefinita per sapere come parlare con l'EJBS della tua sessione. Per far funzionare tutto ciò, è necessario aggiungere un po 'di convenzione e specificare tale convenzione / contratto nelle interfacce. I repository fanno tutto il CRUD e dovrebbero fare di più solo se assolutamente necessari. Se dicessi "I miei DAOS sono i miei repository", sarei d'accordo.
  4. Quindi, per continuare con questo. È necessario qualcosa per incapsulare l'unità di lavoro espressa in UseBizLayer. A questo livello penso che la natura sia che tu sia bloccato a scrivere codice che sarà tutto uno script di transazione. Stai creando una separazione di responsabilità e stato. Questo è in genere il modo in cui l'ho visto fare nei sistemi Java EE come una sorta di architettura predefinita. Ma non è orientato agli oggetti. Vorrei provare a esplorare il modello e vedere se almeno potessi provare a rendere comuni alcuni dei comportamenti scritti in BizClasses.
  5. Un altro approccio che ho usato prima è quello di sbarazzarmi delle classi BizLayer e quindi delegare le chiamate dal Dominio ai Repository / DAO effettivi che eseguono l'operazione. Tuttavia, ciò potrebbe richiedere alcuni investimenti nella costruzione di infrastrutture. Ma potresti fare molto con un framework come Spring e usare alcuni concetti di AOP per far funzionare bene e ridurre la quantità di infrastruttura personalizzata necessaria.

Altri suggerimenti

dal momento che stai implementando classi e oggetti, la tua soluzione sarà OO indipendentemente da come si sovrappongono le cose - potrebbe non essere molto ben strutturata a seconda della situazione / esigenze! ; -)

per quanto riguarda la tua domanda specifica, in alcuni casi sarebbe logico che validateUserName appartenesse alla classe User, poiché ogni utente vorrebbe avere un nome valido. Oppure puoi avere una classe di utilità di convalida supponendo che altre cose abbiano nomi che usano le stesse regole di convalida. Lo stesso vale per l'email. Puoi dividerli in classi NameValidator e EmailValidator, che sarebbe un'ottima soluzione se fossero usati molto. Potresti anche fornire una funzione validateUserName sull'oggetto User che ha appena chiamato il metodo della classe di utilità. Tutte queste sono soluzioni valide.

Una delle grandi gioie di OOD / OOP è che quando il design è giusto, sai che è giusto, perché molte cose escono dal modello e puoi farlo non si poteva fare prima.

In questo caso farei le classi NameValidator e EmailValidator, perché sembra probabile che altre entità avranno nomi e indirizzi e-mail in futuro, ma fornirei funzioni validateName e validateEmailAddress sulla classe User perché ciò rende l'interfaccia più conveniente per gli oggetti biz da usare.

il resto dei proiettili "noi non vogliamo" sono corretti; non sono solo necessari per una corretta stratificazione, ma sono anche necessari per un design OO pulito.

stratificazione e OO vanno di pari passo sulla base di una separazione delle preoccupazioni tra gli strati. Penso che tu abbia l'idea giusta, ma avrai bisogno di alcune classi di utilità per validazioni comuni come presentate

Pensa a come svolgere queste attività se non ci fosse un computer e modella il tuo sistema in questo modo.

Esempio semplice ... Il cliente compila un modulo per richiedere un widget, lo consegna a un dipendente, il dipendente verifica l'identità del cliente, elabora il modulo, ottiene un widget, fornisce il widget e una registrazione della transazione al cliente e tiene traccia della transazione da qualche parte per l'azienda.

Il client memorizza i propri dati? No, l'impiegato lo fa. Quale ruolo assume il dipendente quando memorizza i dati del cliente? Client Records Keeper.

Il modulo verifica che sia stato compilato correttamente? No, l'impiegato lo fa. Quale ruolo assume il dipendente quando lo sta facendo? Form Processor.

Chi fornisce al client il widget? Il dipendente che funge da distributore di widget

E così via ...

Per inserire questo in un'implementazione Java EE ...

Il Servlet agisce per conto del Cliente, compilando il modulo (estraendo i dati dalla richiesta HTTP e creando l'oggetto Java appropriato) e passandoli al dipendente appropriato (EJB), che quindi fa con il modulo ciò di cui ha bisogno da fare. Durante l'elaborazione della richiesta, potrebbe essere necessario che il bean passi a passarlo a un altro bean specializzato in diverse attività, parte delle quali includerebbe l'accesso / immissione delle informazioni da / allo storage (livello dati). L'unica cosa che non dovrebbe essere mappata direttamente all'analogia dovrebbe essere la specificità su come i tuoi oggetti comunicano tra loro e su come il tuo livello dati comunica con la tua memoria.

Anch'io ho avuto gli stessi pensieri.

Nel MVC tradizionale la cosa più importante è separare la vista dalle porzioni di Modello e Controller. Sembra essere una buona idea separare il controller e il modello semplicemente perché puoi finire con oggetti modello gonfio:


public class UserModel extends DatabaseModel implements XMLable, CRUDdy, Serializable, Fooable, Barloney, Baznatchy, Wibbling, Validating {
  // member fields
  // getters and setters
  // 100 interface methods
}

Considerando che puoi avere controller separati (o interi schemi) per molte delle interfacce sopra, e sì, è piuttosto di natura procedurale ma immagino che sia così che funzionano le cose in questi giorni. In alternativa puoi capire che alcune delle interfacce stanno facendo la stessa cosa (CRUDdy - archiviazione e recupero del database, serializzabile - lo stesso in un formato binario, XMLable, lo stesso in XML) quindi dovresti creare un unico sistema per gestirlo, con ogni potenziale backend è un'implementazione separata gestita dal sistema. Dio, è scritto davvero male.

Forse c'è qualcosa come " co-classi " che ti consente di avere file sorgente separati per l'implementazione del controller che agiscono come se fossero membri della classe del modello su cui agiscono.

Per quanto riguarda le regole aziendali, spesso lavorano su più modelli contemporaneamente e quindi dovrebbero essere separati.

Penso che questa sia una domanda sulla "separazione delle preoccupazioni". Sembri essere molto più avanti sulla strada giusta con la tua architettura a più livelli, ma forse devi fare di più lo stesso, ovvero creare livelli architettonici all'interno dei tuoi livelli Java EE?

Un DataContainer assomiglia molto al modello Data Transfer Objects (DTO).

Un moderno design OO ha molte piccole classi, ognuna correlata a un piccolo numero di "amici", ad es. tramite composizione. Ciò potrebbe produrre molte più classi e una piastra di cottura Java di quanto tu non sia veramente a tuo agio, ma dovrebbe portare a un design più stratificato e più facile da testare e gestire.

(+1 per la domanda, +1 per la risposta su quando sai di avere il diritto di stratificazione)

Crudamente, il " Non vogliamo " la sezione deve andare. O vuoi che sia giusto, o vuoi che rimanga così com'è. Non serve a nulla non creare una classe quando ne hai bisogno. " Ne abbiamo in abbondanza " è una brutta scusa.

In effetti, è male esporre tutte le classi interne, ma nella mia esperienza, creando una classe per concetto (cioè un Utente, un ID, un concetto di Database ...) aiuta sempre.

Accanto a questo, un modello di facciata non è qualcosa per risolvere l'esistenza di un sacco di classi BizRules, nascoste dietro un'interfaccia ben organizzata e pulita?

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