Pregunta

Estoy escribiendo una serie de aplicaciones pequeñas y sencillas que comparten una estructura común y la necesidad de hacer algunas de las mismas cosas de la misma manera (por ejemplo, la tala, la configuración de la conexión de base de datos, la configuración del entorno) y estoy buscando algunos consejos en la estructuración de los componentes reutilizables. El código está escrito en un lenguaje fuertemente y tipos estáticos (por ejemplo Java o C #, he tenido que resolver este problema en ambos). Por el momento tengo esto:

abstract class EmptyApp //this is the reusable bit
{
   //various useful fields: loggers, db connections

   abstract function body()
   function run()
   {
        //do setup
        this.body()
        //do cleanup
   }
}

class theApp extends EmptyApp //this is a given app
{
   function body()
   {
        //do stuff using some fields from EmptyApp
   }

   function main()
   {
        theApp app = new theApp()
        app.run()
   }
 }

¿Hay una mejor manera? Tal vez la siguiente manera? Tengo problemas para sopesar las ventajas y desventajas ...

abstract class EmptyApp
{
     //various fields
}

class ReusableBits
{
    static function doSetup(EmptyApp theApp)

    static function doCleanup(EmptyApp theApp)
}

class theApp extends EmptyApp
{
    function main()
    {
         ReusableBits.doSetup(this);
         //do stuff using some fields from EmptyApp
         ReusableBits.doCleanup(this);
    }
}

Una desventaja obvia es que con la opción 2, el 'marco' no puede envolver la aplicación en un bloque try-catch ...

¿Fue útil?

Solución

Siempre he favorecido reutilización a través de la composición (su segunda opción) en lugar de la herencia (la primera opción).

La herencia sólo debe utilizarse cuando hay una relación entre las clases en lugar de para la reutilización de código.

Así que para su ejemplo me gustaría tener clases múltiples ReusableBits haciendo cada cosa que 1 de cada aplicación un hacer uso de como / cuando sea necesario.

Esto permite que cada aplicación para volver a utilizar las partes de su marco de trabajo que son relevantes para la aplicación específica sin ser forzado a tomar todo, permitiendo que las aplicaciones individuales más libertad. Reutilización por herencia a veces puede llegar a ser muy restrictivo si usted tiene algunas aplicaciones en el futuro que no se ajustan exactamente a la estructura que tiene en mente hoy.

También se encuentra la unidad de pruebas y desarrollo impulsado por la prueba mucho más fácil si se rompe el marco arriba en utilidades separadas.

Otros consejos

¿Por qué no hacer la llamada marco en su código personalizable? Por lo que su cliente crea un objeto, y lo inyecta en el marco. Los inicializa marco, las llamadas setup() etc., y luego llama a código de su cliente. Al finalizar (o incluso después de una excepción lanzada), así construidas se llama cleanup() y salidas.

Así que su cliente simplemente implementar una interfaz, como (en Java)

public interface ClientCode {

    void runClientStuff(); // for the sake of argument
}

y el código de marco está configurado con una implementación de este, y llama runClientStuff() siempre que sea necesario.

Así que no lo hace Derivar en el marco de aplicación, sino simplemente proporcionar una clase conforme a un contrato determinado. Puede configurar la instalación de la aplicación en tiempo de ejecución (por ejemplo, qué clase, el cliente proporcionará a la aplicación) ya que no está derivado de la aplicación y por lo que su dependencia no es estática.

La interfaz anteriormente puede ser extendido a tener múltiples métodos, y la aplicación puede llamar a los métodos requeridos en las diferentes etapas del ciclo de vida (por ejemplo, para proporcionar específica del cliente setup / limpieza), pero que es un ejemplo de característica de fluencia: -)

Recuerde, la herencia es sólo una buena opción si todos los objetos que están heredando reutilizar el código dúo a sus similitudes. o si desea que las llamadas sean capaces de interactuar con ellos en la misma fisión. si lo que acabo de mencionar se aplica a usted a continuación, basado en mi experiencia siempre es mejor tener la lógica común en su base de clase / abstracto.

esto es cómo me gustaría volver a escribir su aplicación de ejemplo en C #.

abstract class BaseClass
{
    string field1 = "Hello World";
    string field2 = "Goodbye World";

    public void Start()
    {
        Console.WriteLine("Starting.");
        Setup();
        CustomWork();
        Cleanup();
    }

    public virtual void Setup()
    {Console.WriteLine("Doing Base Setup.");}

    public virtual void Cleanup()
    {Console.WriteLine("Doing Base Cleanup.");}

    public abstract void CustomWork();
}

class MyClass : BaseClass
{
    public override void CustomWork()
    {Console.WriteLine("Doing Custome work.");}

    public override void Cleanup()
    {
        Console.WriteLine("Doing Custom Cleanup");
        //You can skip the next line if you want to replace the
        //cleanup code rather than extending it
        base.Cleanup();
    }

}

void Main()
{
    MyClass worker = new MyClass();
    worker.Start();
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top