Che tipo di progettazione OO utilizzare (esiste un modello di progettazione)?

11

Ho due oggetti che rappresentano un 'Bar / Club' (un luogo in cui si beve / socializza).

In uno scenario ho bisogno del nome della barra, indirizzo, distanza, slogon

In un altro scenario ho bisogno del nome della barra, indirizzo, URL del sito web, logo

Quindi ho due oggetti che rappresentano la stessa cosa ma con campi diversi.

Mi piace usare oggetti immutabili, così tutti i campi sono impostati dal costruttore .

Un'opzione è di avere due costruttori e null gli altri campi i.e:

class Bar {
     private final String name;
     private final Distance distance;
     private final Url url;

     public Bar(String name, Distance distance){
          this.name = name;
          this.distance = distance;
          this.url = null;
     }

     public Bar(String name, Url url){
          this.name = name;
          this.distance = null;
          this.url = url;
     }

     // getters
}

Non mi piace perché dovresti controllare il nullo quando usi i getter

Nel mio esempio reale il primo scenario ha 3 campi e il secondo scenario ha circa 10, quindi sarebbe un vero dolore avere due costruttori , la quantità di campi che dovrei dichiarare null e quindi quando l'oggetto è in uso non sapresti quale Bar tu usi e quindi quali campi sarebbero nulli e quali no.

Quali altre opzioni ho?

Due classi chiamate BarPreview e Bar ?

Qualche tipo di ereditarietà / interfaccia?

Qualcos'altro che è fantastico?

    
posta Blundell 05.07.2012 - 23:31
fonte

9 risposte

9

I miei pensieri:

Una "Barra", come rappresentato nel tuo dominio, ha tutte le cose che possono essere necessarie in entrambi i posti: nome, indirizzo, URL, logo, slogan e "distanza" (sto indovinando dal luogo del richiedente ). Pertanto, nel tuo dominio, dovrebbe esserci una classe "Bar" che è la fonte autorevole di dati per una barra, indipendentemente da dove verranno utilizzati successivamente i dati. Questa classe dovrebbe essere mutabile, in modo che le modifiche ai dati della barra possano essere apportate e salvate quando necessario.

Tuttavia, ci sono due punti in cui i dati di questo oggetto Bar sono necessari, ed entrambi hanno bisogno solo di un sottoinsieme (e non vuoi che i dati vengano modificati). La solita risposta è un "oggetto di trasferimento dati" o DTO; un POJO (semplice oggetto 'Java') contenente i getter di proprietà immutabili. Questi DTO possono essere prodotti chiamando un metodo sull'oggetto del dominio Bar principale: "toScenario1DTO ()" e "toScenario2DTO ()"; i risultati sono un DTO idratato (il che significa che è necessario utilizzare il costruttore lungo e complicato in un unico punto).

Se hai mai avuto bisogno di inviare i dati alla classe del dominio principale (per aggiornarlo, qual è il punto dei dati se non puoi cambiarlo se necessario per riflettere lo stato attuale del mondo reale?), potresti costruire uno dei DTO o utilizzare un nuovo DTO mutabile e restituirlo alla classe Bar utilizzando un metodo "updateFromDto ()".

EDIT: per fornire un esempio:

public class Bar {
     private String name;
     private Address address; 
     private Distance distance;
     private Url url;
     private Image logo;
     private string Slogan;

     public OnlineBarDto ToOnlineDto()
     {
         return new OnlineBarDto(name, address, url, logo);
     }

     public PhysicalBarDto ToPhysicalDto()
     {
         return new PhysicalBarDto(name, address, distance, slogan);
     }

     public void UpdateFromDto(PhysicalBarDto dto)
     {
         //validation logic here, or mixed into assignments

         name = dto.Name;
         address = dto.Address;
         distance = dto.Distance;
         slogan = dto.Slogan;
     }

     public void UpdateFromDto(OnlineBarDto dto)
     {
         //Validate DTO fields before performing assignments

         name = dto.Name;
         address = dto.Address;
         url= dto.Url;
         logo = dto.Logo;
     }

     // getters/setters - As necessary within the model and data access layers;
     // other classes can update the model using DTOs, forcing validation.
}

public class PhysicalBarDto
{
     public final String Name;
     public final Address Address;
     public final Distance Distance;
     public final String Slogan;

     public PhysicalBarDto(string Name, Address address, Distance distance, string slogan) 
     { //set instance fields using parameter fields; you know the drill }
}

public class OnlineBarDto
{
     public final String Name;
     public final Address Address;
     public final Image Logo;
     public final Url Url;

     public OnlineBarDto(string Name, Address address, Url url, Image logo) 
     { //ditto }
}

Le classi Indirizzo, Distanza e Url dovrebbero essere immutabili o se utilizzate nel DTO dovrebbero essere sostituite con controparti immutabili.

    
risposta data 06.07.2012 - 00:13
fonte
5

Se ti interessa solo un sottoinsieme delle proprietà e vuoi assicurarti che non vengano confusi, crea due interfacce e usale per parlare con il tuo oggetto di base.

    
risposta data 06.07.2012 - 05:37
fonte
3

Il Builder Pattern (o qualcosa di simile ad esso) potrebbe essere utile qui.

Avere oggetti immutabili è una cosa ammirevole, ma la realtà è che con Reflection in Java, nulla è veramente sicuro; -).

    
risposta data 06.07.2012 - 00:08
fonte
3

Il punto chiave qui è la differenza tra quale "barra" è e come la si usa in uno o in un altro contesto.

The Bar è una singola entità nel mondo reale (o un mondo artificiale, come un gioco), e solo UN'Oggetto di oggetto dovrebbe rappresentarlo. In qualsiasi momento, quando non si crea quell'istanza da un segmento di codice, ma si carica da un file di configurazione o da un database, questo sarà più evidente.

(Per essere ancora più esoterici: ogni istanza di Bar ha un ciclo di vita diverso dall'oggetto che lo rappresenta quando viene eseguito il programma. Anche se si dispone di un codice sorgente che crea quell'istanza, significa che l'entità Bar come è descritta , "esiste" in uno stato dormiente nel tuo codice sorgente e "risveglia" quando quel codice lo crea effettivamente nella memoria ...)

Ci scusiamo per il lungo periodo, ma spero che questo chiarisca il mio punto. Hai UNA BAR classe con tutti gli attributi che ti sarebbero mai necessari e una istanza che rappresenta ciascuna entità Bar. Questo è corretto nel tuo codice e indipendente da come vuoi vedere la stessa istanza in contesti diversi .

Quest'ultimo può essere rappresentato da due diverse interfacce , che contengono i metodi di accesso richiesti (getName (), getURL (), getDistance ()) e la classe Bar dovrebbe implementare entrambi. (E forse la "distanza" cambierà in "posizione", e getDistance () diventa un calcolo da un'altra posizione :-))

Ma la creazione è per l'entità Bar e non per il modo in cui si desidera utilizzare quell'entità: un costruttore, tutti i campi.

MODIFICATO: posso scrivere codice! : -)

public interface Place {
  String getName();
  Address getAddress();
}

public interface WebPlace extends Place {
   URL getUrl();
   Image getLogo();
}

public interface PhysicalPlace extends Place {
  Double getDistance();
  Slogon getSlogon();
}

public class Bar implements WebPlace, PhysicalPlace {
  private final String name;
  private final Address address;
  private final URL url;
  private final Image logo;
  private final Double distance;
  private final Slogon slogon;

  public Bar(String name, Address address, URL url, Image logo, Double distance, Slogon slogon) {
    this.name = name;
    this.address = address;
    this.url = url;
    this.logo = logo;
    this.distance = distance;
    this.slogon = slogon;
  }

  public String getName() { return name; }
  public Address getAddress() { return address; }
  public Double getDistance() { return distance; }
  public Slogon getSlogon() { return slogon; }
  public URL getUrl() { return url; }
  public Image getLogo() { return logo; } 
}
    
risposta data 06.07.2012 - 22:02
fonte
1

Pattern appropriato

Quello che stai cercando è più comunemente indicato come Null Object Pattern . Se non ti piace il nome puoi chiamarlo Undefined Value Pattern , stessa semantica etichetta diversa. A volte questo pattern è chiamato Poison Pill Pattern .

In tutti questi casi, l'oggetto è un sostituto o sostituto di un Default Value invece di null. It doesn't replace the semantic of null but makes it easier to work with the data model in a more predictable way because null 'ora mai essere uno stato valido.

È un Pattern in cui si riserva un'istanza speciale di una data classe per rappresentare un'opzione altrimenti null come Default Value . In questo modo non devi eseguire il confronto con null , puoi controllare l'identità con la tua istanza NullObject nota. Puoi tranquillamente chiamare metodi su di esso e simili senza preoccuparti di NullPointerExceptions .

In questo modo sostituisci i tuoi null assegnamenti con le relative istanze rappresentative NullObject e il gioco è fatto.

Analisi corretta orientata agli oggetti

In questo modo puoi avere un Interface comune per il polimorfismo e avere ancora protezione dal dover preoccuparsi dell'assenza di dati nelle implementazioni specifiche dell'interfaccia. Quindi alcuni Bar potrebbero non avere la presenza sul Web e alcuni potrebbero non disporre di dati sulla posizione al momento della costruzione. Null Object Patter ti consente di fornire un valore predefinito per ognuno di questi che è un marker per quei dati che dice la stessa cosa, non è stato fornito nulla qui, senza avere l'accordo con il controllo di NullPointerException in tutto il luogo.

Design orientato agli oggetti appropriato

Prima di avere un'implementazione abstract che è un super set di tutti gli attributi che sia Bar che Club share.

class abstract Establishment 
{
     private final String name;
     private final Distance distance;
     private final Url url;

     public Bar(final String name, final Distance distance, final Url url)
     {
          this.name = name;
          this.distance = distance;
          this.url = url;
     }

     public Bar(final String name, final Distance distance)
     {
          this(name, distance, Url.UNKOWN_VALUE);
     }

     public Bar(final String name, final Url url)
     {
          this(name, Distance.UNKNOWN_VALUE, url);
     }

     // other code
}

Quindi puoi implementare le sottoclassi di questa classe Establishment e aggiungere solo le cose specifiche necessarie per ciascuna delle classi Bar e Club che non si applicano alle altre.

Persistenza

Questi oggetti segnaposto se costruiti correttamente possono essere memorizzati in modo trasparente in un database senza alcuna gestione speciale.

Prova futura

Se hai deciso di saltare sul carro di Inversion of Control / Iniezione di dipendenza dopo, questo modello rende facile iniettare anche questi oggetti marker.

    
risposta data 06.07.2012 - 00:53
fonte
0

Penso che il problema sia che non stai modellando una barra in nessuno di questi scenari (e stai modellando due diversi problemi, oggetti, ecc.). Se vedo un bar di classe mi aspetterei alcune funzionalità legate alle bevande, ai menu, ai posti disponibili e il tuo oggetto non ha nulla di tutto ciò. Se vedo il comportamento degli oggetti stai modellando le informazioni su un istituto. Bar è quello che stai usando per questo in questo momento, ma non è il comportamento intrinseco che stanno implementando. (In un altro contesto: se stai modellando un matrimonio, avrai due variabili di istanza Persona moglie, Persona marito, una moglie è il ruolo attuale che stai dando a quell'oggetto in quel momento, ma l'oggetto è ancora una Persona). Farei qualcosa del genere:

class EstablishmentInformation {
     private final String name;

     public EstablishmentInformation(String name){
          this.name = name;
     }

     // getters
}

class EstablishmentLocationInformation {
    EstablishmentInformation establishmentInformation;
     private final Distance distance;

     public EstablishmentLocationInformation (String name, Distance distance){
          this.establishmentInformation = new EstablishmentInformation(name)
          this.distance = distance;
     }
}

class EstablishmentWebSiteInformation {
    EstablishmentInformation establishmentInformation;
     private final Url url;

     public EstablishmentWebSiteInformation(String name, Url url){
          this.establishmentInformation = new EstablishmentInformation(name)
          this.url = url;
     }
}
    
risposta data 07.07.2012 - 20:43
fonte
-1

Non c'è davvero bisogno di complicare troppo questo. Hai bisogno di due diversi tipi di oggetti? Crea due classi.

class OnlineBar {
     private final String name;
     private final Url url;
     public OnlineBar(String name, Url url){
          this.name = name;
          this.url = url;
     }

     // ...
}
class PhysicalBar {
     private final String name;
     private final Distance distance;
     public PhysicalBar(String name, Distance distance){
          this.name = name;
          this.distance = distance;
     }
     //...
}

Se hai bisogno di operare su di essi allo stesso modo, prendi in considerazione l'aggiunta di un'interfaccia o l'uso di reflection.

    
risposta data 06.07.2012 - 01:31
fonte
-1

La mia risposta a chiunque abbia questo tipo di problema è quella di scomposizione in fasi gestibili .

  1. In primo luogo, crea solo due classi BarOne e BarTwo (o chiamale entrambe Bar ma in pacchetti diversi)
  2. Inizia ad usare gli oggetti come classi separate, non preoccuparti della duplicazione del codice per ora. Noterai quando ti capovolgi (metodi duplicati) dall'uno all'altro
  3. Potresti scoprire che non sono in alcun modo correlati e quindi dovresti chiederti che entrambi sono davvero un bar se non rinominare la classe che causa l'errore in ciò che rappresenta ora
  4. Se trovi campi o comportamenti comuni, puoi estrarre interface o superclass con il comportamento comune
  5. Una volta che hai interface o superclass puoi creare un builder o factory per creare / recuperare gli oggetti di implementazione

(4 e 5 sono le risposte dell'altra a questa domanda)

    
risposta data 07.07.2012 - 20:12
fonte
-2

Hai bisogno di una classe base, ad esempio Posizione che ha un nome e un indirizzo . Ora hai due classi Bar e BarPreview estendi la classe base Posizione . In ogni classe si inizializzano le variabili comuni di super classe e quindi le variabili univoche:

public class Location {
    protected final String name;
    protected final String address:

    public Location (String locName, String locAddress) {
    name = locName;
    address = locAddress
    }

}

public class Bar extends Location {
    private final int dist;
    private final String slogan;

    public Bar(String barName, String barAddress,
               int distance, String barSlogan) {
    super(locName, locAddress);
    dist = distance;
    slogan = barSlogan;
    }
}

E simili per la classe BarPreview ..

Se ti fa dormire meglio di notte, sostituisci tutte le istanze di Posizione nel mio codice con AnythingououhinkWouldBeAnAppropriateNameForAThingThatABarExtendsSuchAsFoodServicePlace - ffs.

    
risposta data 06.07.2012 - 00:07
fonte