Domanda

Sono perso e ho bisogno di una guida divina.

Prima delle cose: supponi di avere alcune interfacce ben e-nato:

class IProduct
{
public:
    virtual void DoThings();
}


enum ProductType
{
   ...
}


class IProducer
{
public:
    virtual IProduct* Produce( ProductType type );
}


class IConsumer
{
public:
    virtual void Consume( IProduct* product );
}

È ancora semplice: fabbrica astratta, consumatore astratto che invocherà l'interfaccia, fornita volentieri da quei iprodutti appena spazzati. Ma ecco la parte difficile. Supponiamo che ci siano due (o più) gruppi in cemento paralleli:

class ConcreteProducerA : public IProducer { ... }
class ConcreteConsumerA : public IConsumer { ... }
class ConcreteProductA : public IProduct { ... }

class ConcreteProducerB : public IProducer { ... }
class ConcreteConsumerB : public IConsumer { ... }
class ConcreteProductB : public IProduct { ... }

E quei concreti sono cose reeealy diverse. Come una parte dello spazio per lo spazio (con una fabbrica di parti e una catena di assemblaggio della navetta) e sacchi di verdure (con una fattoria e .. idk, che consumano quelle verdure?). Eppure hanno quella cosa in generale: Dothings (). Fai finta che sia, come, packandsend () o serializza (), o smalti (), qualunque cosa tu voglia. Niente di concreto, ma legittimo su cui basare una gerarchia. Ma quelli hanno ancora più differenze, rispetto alle generalità. Quindi quei concreteconsumatori tendono ad usarli in modo diverso. In modo così diverso, che, in effetti, devono assolutamente essere sicuri, che si suppone che si presenti un tipo concreto.

Quindi ecco il problema: sto costringendo gli utenti di quella gerarchia a ridurre l'iPduct per concreti -produzione nelle loro prevalenti virtuali in questo momento. E questo mi ha infastidito duramente. Sento che mi manca qualcosa: un grosso difetto nella gerarchia, una certa mancanza di conoscenza del modello, qualcosa. Voglio dire, posso assicurarmi che concreteconsumerB riceve sempre CalcreteProductB, ma è ancora un decreto. E useresti mai un framework, che passa sempre in giro (void*) e ti costringe a lanciarlo su quando pensi che ti venga in mente?

Soluzioni che ho già considerato:

  1. Tunnel TUTTI I CONTTERE interfacciano nel Iprodotto. Ma quel prodotto Gona si trasforma in un blob incontrollabile, che può mangiare (), beeaten (), lancia (), distruggere () e chiunque sappia cos'altro. Quindi questa soluzione non sembra niente di meglio che abbassarmi.
  2. Che i dothings () possono probabilmente essere disaccoppiati dallo iprodotto in un altro gestore, che sarà in grado di accettare tutti i calcestruzzi (simili a visitatori). In questo modo può essere rimosso iprodotto e ci saranno gruppi concreti separati. Ma cosa succede se esiste uno strato semiconcreto, che impila alcune funzionalità comuni per quei gruppi concreti? Come l'etichettatura, il trasformazione, il massaggio, qualunque cosa. Inoltre, quando sarà necessario aggiungere un altro gruppo concreto, sarò costretto a modificare quei visitatori, il che un po 'aumenta l'accoppiamento.
  3. (AB) Usa i modelli. Sembra saggio al momento. Qualcosa sulla falsariga di

    template < typename _IProduct >
    class IConcreteProducer : public IProducer
    {
    public:
        virtual _IProduct* Produce( _IProduct::Type type ) = 0;
        virtual _IProduct::Type DeduceType( ProductType type ) = 0;
        virtual IProduct* Produce( ProductType type )
        {
            return IConcreteProducer<typename _IProduct>::Produce( DeduceType( type ) );
        }
    }
    
    template < typename _IProduct >
    class IConcreteConsumer : public IConsumer
    {
    public:
        virtual void Consume( _IProduct* product ) = 0;
        virtual void Consume( IProduct* product )
        {
            IConcreteConsumer<typename _IProduct>::Consume( (_IProduct*)product );
        }
    }
    

    In questo modo ho il controllo di quel abbassamento, ma è presente.

Comunque, questo problema suona familiare a qualcuno? Qualcuno l'ha visto risolto, o forse Heroyy l'ha risolto da solo? La soluzione C ++ sarebbe fantastica, ma penso che qualsiasi linguaggio di tipo statico sarà sufficiente.

È stato utile?

Soluzione

Eppure hanno quella cosa in generale: Dothings (). Fai finta che sia, come, packandsend () o serializza (), o smalti (), qualunque cosa tu voglia. Niente di concreto, ma legittimo su cui basare una gerarchia.

Solo perché loro Potere essere in una gerarchia, non significa che dovrebbero. Non sono correlati. Non riesco nemmeno a capire quale valore stai aggiungendo a qualsiasi base di codice generalizzando navette e verdure. Se non aggiunge benefici agli utenti, probabilmente stai solo rendendo le cose più contorte su te stesso.

Mi aspetterei di vedere interfacce come le seguenti. Notate che non ereditano da nulla. Se hai un codice condiviso, scrivi classi concrete stupide più semplici che le persone possono riutilizzare per composizione.

template<typename T>
  class Producer {
  public:
    virtual ~Producer() {}
    virtual std::auto_ptr<T> produce() = 0;
  };

template<typename T>
  class Consumer {
  public:
    virtual ~Consumer() {}
    virtual void consume(std::auto_ptr<T> val) = 0;
  };

Quindi mi aspetterei di vedere funzioni concrete per crearle da varie fonti.

typedef Producer<Shuttle> ShuttleProducer;
typedef Consumer<Shuttle> ShuttleConsumer;

std::auto_ptr<ShuttleProducer> GetShuttleProducerFromFile(...);
std::auto_ptr<ShuttleProducer> GetShuttleProducerFromTheWeb(...);
std::auto_ptr<ShuttleProducer> GetDefaultShuttleProducer();

Probabilmente non esiste uno schema per quello che vuoi fare, sono probabilmente due schemi che stai fondando (termine tecnico) insieme. Non hai tradito perché queste cose dovrebbero condividere una base di codice, quindi possiamo solo indovinare.

Negli scenari più complicati, ti consigliamo di un uso rigorosamente separato dalla creazione. È perfettamente valido avere interfacce diverse che sembrano un po 'simili, ma sono usate in modo diverso.

class Foo {
public:
  virtual ~Foo() {}
  virtual void doStuff() = 0;
  virtual void metamorphose() = 0;
};

class Fu {
public:
  virtual ~Fu() {}
  virtual void doStuff() = 0;
  virtual void transmorgrify() = 0;
};

Altri suggerimenti

Una possibilità è quella di introdurre un secondo livello alla gerarchia calda. Derivare IShuttle da IProduct, e derivano quel gruppo da esso. Quindi aggiungere un IShuttleProducer Ciò produce un IShuttle* invece di IProduct*. Questo va bene, perché C ++ consente tipi di restituzione covariante per funzioni virtuali ... Finché il nuovo tipo di ritorno deriva dall'originale, è ancora considerato un override.

Ma il tuo design probabilmente ha bisogno di un po 'di ripensamento in entrambi i casi.

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