Pregunta

Estoy perdido y necesito una guía divina.

Lo primero es lo primero: suponga que tiene algunas interfaces bien solares:

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


enum ProductType
{
   ...
}


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


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

Es simple aún simple: fábrica abstracta, consumidor abstracto que invocará la interfaz, proporcionada con mucho gusto por esos iProducts recién desarrollados. Pero aquí viene la parte difícil. Suponga que hay dos (o más) grupos concretos paralelos:

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

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

Y esos concretos son cosas diferentes. Como una pieza de desplazamiento espacial (con una fábrica de piezas y una línea de ensamblaje de transporte) y bolsas de verduras (con una granja y ... idk, ¿quién consumiría esas verduras?). Sin embargo, tienen esa cosa en general: Dothings (). Finge que es, como, packandsend (), o serialize (), o desechar (), lo que quieras. Nada concreto, pero legítimo para basar una jerarquía. Pero esos todavía tienen más diferencias, que las generalidades. Entonces, esos concreteconsumadores tienden a usarlos de manera diferente. Tan diferente, que, de hecho, deben estar seguros, que se supone que se supone que el tipo concreto.

Así que aquí está el problema: estoy forzando a los usuarios de esa jerarquía a la ipoducto de flujo a la reducción de concretos en sus anulaciones virtuales en este momento. Y eso me molesta mucho. Siento que me falta algo: un gran defecto en la jerarquía, algo de falta de conocimiento de patrones, algo. Quiero decir, puedo asegurarme de que concreteConsumerb siempre recibe concreteproductB, pero sigue siendo una negligencia. ¿Y alguna vez usarías un marco, que siempre pasa por (sin efecto*) y te obliga a lanzarlo a Whenwer, crees que vendrá a ti?

Soluciones que ya he considerado:

  1. Túnel Todas las interfies de conctretos en iProduct. Pero ese producto se convierte en Blob incontrolable, quién puede comer (), BeeAten (), lanzar (), destruir () y a quien sepa qué más. Así que esta solución no parece nada mejor que derribarme.
  2. Que dothings () probablemente se puedan desacoplar de iproducto a otro manejador, que podrá aceptar todos los concretos (como visitantes). De esa manera, se puede eliminar el iproducto y habrá grupos concretos separados. Pero, ¿qué pasa si hay una capa semiconcreta, que Implementa alguna funcionalidad común para esos grupos concretos? Como etiquetar, transformarse, masajear, lo que sea. Además, cuando sea necesario agregar otro grupo de concreto, me veré obligado a cambiar esa (s) visitante (s), lo que aumenta un poco el acoplamiento.
  3. (ab) Use plantillas. Eso parece sabio en este momento. Algo en la línea de

    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 );
        }
    }
    

    De esta manera, tengo el control de ese descenso, pero está muy presente.

De todos modos, ¿este problema suena familiar para alguien? ¿Alguien lo vio resuelto, o tal vez Heroicaly lo resolvió él mismo? La solución C ++ sería increíble, pero creo que cualquier lenguaje de tipo estadísticas será suficiente.

¿Fue útil?

Solución

Sin embargo, tienen esa cosa en general: Dothings (). Finge que es, como, packandsend (), o serialize (), o desechar (), lo que quieras. Nada concreto, pero legítimo para basar una jerarquía.

Solo porque ellos pueden estar en alguna jerarquía, no significa que deberían. No están relacionados. Ni siquiera puedo entender qué valor está agregando a cualquier base de código generalizando transbordadores y verduras. Si no agrega beneficio a los usuarios, entonces es probable que solo haga que las cosas sean más complicadas de usted mismo.

Esperaría ver interfaces como la siguiente. Observe que no heredan de nada. Si ha compartido código, escriba clases de concreto tontas más simples que las personas puedan reutilizar por composición.

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;
  };

Entonces esperaría ver funciones concretas para crearlas a partir de varias fuentes.

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

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

Probablemente no haya un patrón para lo que desea hacer, es probable que sean dos patrones que están lando (término técnico) juntos. No traicionó por qué estas cosas deberían compartir una base de código, por lo que solo podemos adivinar.

Sin embargo, en los escenarios más complicados, querrás separar estrictamente el uso de la creación. Es perfectamente válido tener diferentes interfaces que se ven similares, pero se usan de manera diferente.

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;
};

Otros consejos

Una posibilidad es introducir una segunda capa a la jerarquía caliente. Derivar IShuttle de IProduct, y deriva ese grupo de él. Luego agregue un IShuttleProducer que produce un IShuttle* en vez de IProduct*. Esto está bien, porque C ++ permite tipos de retorno covariantes para funciones virtuales ... Mientras el nuevo tipo de retorno se deriva del original, todavía se considera una anulación.

Pero su diseño probablemente necesita un poco de repensación de cualquier manera.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top