Individuazione delle responsabilità dell'oggetto durante la progettazione di un lettore di libri

2

La maggiore difficoltà che sto riscontrando è trovare le responsabilità di ciascun oggetto da me identificato nel sistema o nel cosiddetto spazio problematico. Sto postulando una descrizione molto semplificata di un lettore di libri.

Questa volta ho pensato a quali azioni possono essere eseguite su un oggetto e quell'azione è diventata il metodo su quell'oggetto, in seguito chi eseguirà quell'azione diventerà il parametro del messaggio / metodo ma non sono sicuro perché in quello in modo che ogni messaggio abbia qualcosa come parametro. Vorrei limitare la discussione a questo livello in modo da ottenere chiare discussioni e risposte.

/*
A user should be able to search a book from a library
bookmark it, favorite it or rate it.

A Book
  1. Can be favorited  (by whom?)
  2. Can be bookmarked (by whom?)
  3. Can be rated      (by whom?)
  4. Can be added to a library?
  5. Can be removed from a library?

A Library
  1. Can be Searched
  2. Can be modified?
    a. Add book
    b. Remove book

Relationship:

A library can have many book (o2m)
A library can have many users and an user can subscribe to many libraries(m2m)
A user can rent many books (o2m) needs a new model (rent)?
*/

interface Book {
  void bookmark(User user);
  void favorite(User user);
  void rate(User user);
}

interface Library {
  List<Book> search();
  int add();
  boolean remove();
  List<Book> all();
}

public class Main {
  public static void main(String... args) {
    System.out.println("Starting application");
  }
}

Questa è la visualizzazione di alto livello e vorrei sapere se questo approccio è corretto?

    
posta CodeYogi 27.09.2017 - 22:58
fonte

3 risposte

3

Non esiste un modo giusto - ci sono molti modi in cui non sarebbe sbagliato. Soprattutto quando si hanno relazioni molti-a-molti, come i preferiti che mettono in relazione i libri con gli utenti.

Quando si tratta di identificare le responsabilità, considererò spesso - a livello di dominio, in termini di concetti e relazioni di dominio - chi possiede le informazioni / conoscenze, che descrive, conserva e deve essere modificato. È il proprietario di quello stato che ha la responsabilità di tenere, distribuire e aggiornare. Quindi, guarderei allo stato essenziale per aiutare a identificare le responsabilità e la loro proprietà.

Guardare al dominio e al modello per i concetti di dominio più naturali: per me, sarebbe più naturale che un utente possieda il proprio elenco di preferiti, piuttosto che un libro che possiede un elenco di utenti che hanno preferito. Quindi, in OOP, modellerei le azioni (fav'ing e unfaving) come azioni dell'oggetto utente piuttosto che azioni di oggetti book.

Tuttavia, potresti scoprire che la tua applicazione non ha solo bisogno di passare dagli utenti ai libri preferiti, ma anche di passare da un libro all'altro degli utenti che hanno preferito, e se è così, potresti volere un elenco di questo tipo nell'oggetto del libro. Non importa quanto sia utile, a mio avviso, ciò costituirebbe dati derivati piuttosto che dati essenziali , quindi vorrei probabilmente non supporta le azioni di aggiornamento / i comandi da quella direzione (ad es. dall'oggetto del libro).

(Nella persistenza è probabile che modelliamo una tabella di relazioni (di prima classe) che supporta l'attraversamento in entrambe le direzioni.)

Per quanto riguarda i segnalibri e le valutazioni, penso che questi siano concetti di dominio fondamentali e meritino le loro entità e associazioni (e azioni).

Nelle raccolte, come la libreria, di solito lasciamo che un'entità che rappresenta la raccolta gestisca l'ammissione e la rimozione dei membri piuttosto che l'aggiunta del membro alla raccolta. Questo perché la responsabilità di preservare e modificare lo stato associato alla relazione (appartenenza alla collezione) si basa più naturalmente sulla collezione piuttosto che sul membro.

    
risposta data 28.09.2017 - 01:37
fonte
2

Inizio a decomporre il mio dominio con una storia di design. Di cosa parla il mio dominio? Cos'è importante? Quali sono i concetti principali? Quali azioni dovrebbero essere fatte (senza identificare da chi al momento)? Quali sono i confini del mio dominio, cosa sto modellando e cosa non sto modellando?

Successivamente identifico i concetti principali che formano il mio dominio. Probabilmente alcune relazioni importanti potrebbero essere illustrate su una rete semantica. Quindi arriviamo con oggetti candidati. Dovrebbero avere delle chiare responsabilità per essere utili.

Allora vengo con casi d'uso. I miei oggetti candidati apparentemente vi prendono parte. È così che posso avere un'idea di quale responsabilità dovrebbe avere ogni oggetto.

Cose che tengo a mente mentre modellino lo spazio dei miei problemi:

  • Gli oggetti sono attivi e intelligenti. Possiedono tutte le risorse di cui hanno bisogno per attuare le proprie responsabilità.
  • Le responsabilità dovrebbero essere dichiarate in una voce attiva. È una conseguenza di una dichiarazione precedente.

Quindi parla del tuo dominio.

Design story

Sto modellando una biblioteca. Qualunque persona può visitarlo. Dopo la registrazione, dovrebbe essere in grado di cercare un libro da una libreria, aggiungerlo ai segnalibri, preferirlo o votarlo.

Quindi da questa storia di design concludo che non sto modellando un dominio di nuove entrate di libri. Apparentemente è un sottodominio separato con le proprie responsabilità. Esso rappresenta un diverso business capacità , in altre parole .

Oggetti candidati

Una biblioteca, un utente, un libro.

Use cases

A user searches for a book.

Potrebbe sembrare che sia una responsabilità dell'utente. Ma il pensiero degli oggetti implica che gli oggetti siano attivi e incapsulati. Un utente non può scavare nella parte interna della biblioteca. La biblioteca è responsabile per le informazioni che possiede. Si correla con il concetto esperto di informazioni di GRASP . Quindi, se un utente vuole cercare un libro, dovrebbe chiedere una biblioteca.

A user bookmarks a book.

Un libro potrebbe non sapere nulla che sia segnato da qualcuno. Non è affar suo. Solo un utente si preoccupa di aver inserito qualche libro nei preferiti. Quindi è sicuramente responsabilità dell'utente.

... e così via.

Codice

interface User
{
    public function bookmark(Book $book);

    public function addToFavourites(Book $book);

    public function rate(Book $book);
}

interface Library
{
    public function search(Book $book);
}

Ulteriori pensieri

È probabile che l'azione di ogni utente - aggiungendo ai preferiti, ai segnalibri o alla valutazione, finisca con il proprio concetto con le proprie responsabilità. Quindi l'implementazione potrebbe essere la seguente:

class Bookmark
{
    private $book;
    private $user;

    public function __construct(Book $book, User $user)
    {
        $this->book = $book;
        $this->user = $user;
    }

    public function highlight()
    {
        // ...
    }
}

class Rate
{
    private $book;
    private $user;

    public function __construct(Book $book, User $user)
    {
        $this->book = $book;
        $this->user = $user;
    }

    public function discard()
    {
        // ...
    }
}

Questi concetti potrebbero essere utilizzati nel punto di ingresso dell'applicazione, quindi probabilmente User potrebbe essere scartato di queste responsabilità.

dei modi per decomporsi un dominio.

    
risposta data 04.11.2017 - 20:33
fonte
1

Da una visualizzazione di alto livello sembra che tu abbia modellato correttamente questa attività, tuttavia esiste una regola che non hai rispettato: dovresti fare riferimento ad altri aggregati solo per ID. Queste regole ti aiuteranno a separare meglio gli Aggregati perché non puoi accedere alle altre proprietà di Aggregazione da un aggregato.

Quindi il tuo codice dovrebbe apparire in questo modo:

interface Book { 
    void bookmark(UserId userId); 
    void favorite(UserId userId); 
    void rate(UserId userId); 
} 
interface Library { 
    //List<Book> search(); - // this should be a domain service
    void addBook(BookId bookId); // CQS - command
    void removeBook(); // CQS - command
    List<BookId> allBooks(); 
} 

La ricerca nella libreria dovrebbe essere una responsabilità del servizio di dominio perché coinvolge più aggregati e viene utilizzata solo per la lettura.

Dovresti anche seguire il modello CQS .

    
risposta data 28.09.2017 - 07:28
fonte