Mapping di oggetti di business ai loro relatori

2

Questa domanda deriva dal problema più concreto chiesto qui

Il problema che vorrei discutere qui sembra essere un modello molto comune nell'ingegneria del software. Molte applicazioni utilizzano l'architettura a due livelli: il livello aziendale e uno strato dell'interfaccia utente. Vorrei sapere qual è il modo OOP più pulito di mappare gli oggetti di business ai loro relatori UI (o alle visualizzazioni, se preferisci). Userò Java per illustrare le mie difficoltà.

Supponiamo di avere una struttura grafica in cui il livello aziendale contiene forme come Circle , Rectangle ecc. Tutte sono derivate da un'interfaccia comune Shape . Nell'interfaccia utente abbiamo CirclePresenter , RectanglePresenter ecc. Tutti derivati da ShapePresenter . La domanda è come mappare un'istanza di Shape a un'istanza di ShapePresenter . Vorrei che il sistema fosse estensibile in modo che gli utenti del framework possano aggiungere le proprie forme e relatori senza la necessità di ricompilare.

Ho visto e posso anche immaginare diversi approcci, ma tutti mi sembrano come hack che distruggono la bellezza del buon OOP:

Metodo 1: utilizzo dei visitatori

Se abbiamo un visitatore come questo

interface ShapeVisitor<T> {
  public T visitCircle(Circle circle);
  public T visitRectangle(Rectangle rect);
}

possiamo usarlo per creare fabbriche di presentatori come questa:

class Factory implements ShapeVisitor<ShapePresenter> {
  public ShapePresenter visitCircle(Circle circle) {
    return new CirclePresenter(circle);
  }

  ...
}

Questo modello non consente di estendere il framework poiché è necessario aggiungere nuovi metodi al visitatore ogni volta che viene aggiunta una nuova forma. Non sono stato in grado di presentare personalmente un'implementazione dei visitatori estensibile, quindi questo approccio mi è sembrato inutile. Tuttavia, qui è un tentativo di creare un visitatore riflessivo, che afferma di risolvere questo problema esatto. Se questo non è l'hack hacking di tutti gli hack di Java, allora non so cosa sia ...

Approccio 2: Ispirato da Eclipse

Gli schemi di diagrammi di Eclipse come GEF e GMF affrontano questo esatto problema di forme e presentatori. GMF, si basa sulla generazione di codice in cui è necessario specificare le forme e i presentatori in una lingua diversa e quindi il generatore sputa classi Java con tonnellate di controlli instanceof. Questa soluzione non mi sembra molto pulita, per non parlare del fatto che il generatore limita la tua espressività. GEF, d'altra parte, tiene traccia della mappatura di shape- > presenter utilizzando HashMaps. Quando aggiungi un nuovo presentatore, devi aggiungere una voce al registro centrale che è gestita da qualche oggetto dio incapsulabile come PresentationLayer .

L'approccio GEF ha più senso per me, ma non ne sono ancora soddisfatto dato che aggiunge troppi codici di gestione al framework. Sembra che il 20% del codice si concentri sul problema in questione, definendo e disegnando forme, mentre l'altro 80% è in grado di garantire l'estensibilità. Quindi in questo caso la maggior parte del codice è composta da classi trascendentali come AbstractFactoryExtensionPoint , CompositeAdapterServiceLocator o GenericDecoratorRegistry . Ho solo voluto ingenuamente disegnare rettangoli e cerchi in modo estensibile e ho finito con un gruppo di AbstractAdaptableCompositeRegistryServiceLocator s che nessuno può capire senza leggere la documentazione di centinaia di pagine.

Qualche idea?

    
posta lishaak 25.05.2017 - 21:43
fonte

2 risposte

1

Posso riferire ai problemi che incontri con Approach 1 in un framework e concordare con te che non è un approccio estensibile per un framework. Rompe il Principio Aperto / Chiuso . Questo è uno dei motivi principali per evitarlo.

Non ho familiarità con Eclipse ma ottengo l'idea di base dell'approccio GEF dalla descrizione. Dalla mia esperienza di lavoro in un framework C ++ su larga scala, penso che sia l'approccio migliore per un framework.

L'elenco di problemi che devono essere affrontati in un framework estendibile:

  1. Identifica un set di tipi estensibile.
  2. Identifica un'operazione (ottieni qualcosa, imposta qualcosa, fai qualcosa, ecc.) che può essere applicata a qualsiasi istanza dei tipi di livello foglia.
  3. Fornire un meccanismo nel framework per consentire la registrazione di un agente (classe o funzione) che può eseguire l'operazione per un tipo di livello foglia.
  4. Aggiungi un nuovo tipo.
  5. Aggiungi l'agente (classe o funzione) che può eseguire l'operazione per il nuovo tipo.
  6. Registra l'agente con il framework.
  7. Implementa una funzione nel framework per eseguire l'operazione su un oggetto. L'implementazione assicura che venga utilizzato l'agente giusto per l'oggetto.

Quando si tratta di artefatti del codice, è necessario disporre del codice nel framework per 1, 2, 3 e 7 e codice nell'estensione per 4, 5 e 6.

Qualunque cosa tu faccia, non puoi evitare 4 e 5. Inoltre, non puoi ridurre la quantità di codice in 4 e 5. Il tuo obiettivo deve essere quello di rendere il codice per il 6 il più semplice possibile.

In C ++, usiamo un modello che ha servito bene il mio team.

  1. Usiamo un namespace specifico per un file .cpp.
  2. In quel namespace , definiamo una classe chiamata Initializer .
  3. Definiamo un file con scope static istanza di Initializer .
  4. Il costruttore di Initializer contiene il codice che si occupa dell'inizializzazione appropriata per il file .cpp. Molte volte, registra una classe / funzione con il framework per il tipo associato al file .cpp.

Ad esempio, in Square.cpp, puoi usare qualcosa sulla falsariga di:

namespace Square_NS
{
   struct Initializer
   {
      Initializer();
   };

   void drawSquare(Square& square)
   {
      // Do the needful to draw a square.
   }
}
using namespace Square_NS;

static Initializer initializer;

Initializer::Initializer()
{
   ShapePresenter::registerFunction<Square>(drawSquare);
}

Spero che tu possa tradurlo in modo appropriato nella tua lingua preferita.

    
risposta data 25.05.2017 - 23:05
fonte
1

Ho letto il tuo domanda originale Inoltre, e sembra partire da una supposizione errata, che la presentazione e la "business logic" devono essere completamente separate.

Non esiste una regola di questo tipo nell'orientamento agli oggetti. In effetti, una buona argomentazione potrebbe essere che separare completamente queste cose è in realtà un anti-pattern (spesso ripetuto). L'argomento è che tu spezzi parti coesive dall'oggetto.

Normalmente, per presentare qualcosa (nel tuo esempio Cerchio , Rettangolo , ecc.) devi sapere esattamente di cosa si tratta, quali sono le sue proprietà, cosa mezzi . In altre parole, richiede informazioni altrimenti disponibile solo in l'oggetto stesso. È quasi impossibile creare un " CirclePresenter " all'esterno dell'oggetto Cerchio , senza aprire il Cerchio (rendendo disponibili i suoi interni, sconfiggendo l'incapsulamento ).

Quindi, qual è un'alternativa OO? Bene, l'oggetto dovrebbe contenere almeno una nozione astratta di la sua presentazione. Ad esempio:

public interface Shape {
    void display(Canvas canvas);
}

Una forma può disegnare se stessa.

Ci sono naturalmente molte alternative su come puoi farlo, a seconda di quali siano i requisiti esatti per la tua applicazione. Il display() potrebbe restituire vertici, o triangoli per la presentazione 3D, un'immagine, vettori, javascript, codice html, ecc.

Il tema comune è che non ci dovrebbe essere alcuna agenzia esterna che sappia o si preoccupi degli interni di forme specifiche, e mentre le forme specifiche stesse potrebbero non conoscere le specifiche della presentazione (come i colori usati, le dimensioni, ecc. ), fanno conoscono qualche nozione astratta di presentazione appropriata.

    
risposta data 06.06.2017 - 16:53
fonte

Leggi altre domande sui tag