2010-09-27 15 views
8

estaba escribiendo un montón de código hace unos meses y ahora estoy añadiendo cosas a la misma. Me di cuenta de que escribí un conjunto de funciones que descienden de una clase que tiene aproximadamente 2/3 de sus funciones abstractas y el 1/3 virtual restante.Delphi: Capacidad de mantenimiento virtual vs virtual Resumen

estoy bastante harto de ver:

function descendent.doSomething() : TList; 
begin 
    inherited; 
end; 

cuando tengo esto para la clase base:

function descendent.doSomething() : TList; 
begin 
    result := nil; 
end; 

y Odiaría terminar con:

function descendent.doSomething() : TList; 
begin 

end; 

y luego se preguntan por qué algo no funcionaba.

me gusta usar funciones abstractas porque el compilador le permitirá saber si usted es responsable de obtener un error abstracto porque no implementar algunas funciones.

Mi pregunta es, porque todavía soy un programador de Delphi relativamente nuevo y nunca he tenido que mantener nada en 8 años, vale la pena tomarse el tiempo para prune your code de esta manera (es decir, eliminar las funciones que acaban de heredar en ellos y cambie sus funciones de clase base de abstracto a concreto)

Respuesta

1

Si el código es realmente simple y le resulta difícil de leer y propenso a errores, entonces probablemente sea difícil de leer y propenso a errores. (Por otra parte, si el código es complicado y le resulta difícil de leer, que podría ser su falta de experiencia. Pero no es algo como esto.) Es probable que haría bien en refactorearlo ahora, mientras que la cuestión todavía está fresco en tu mente.

+0

Me pregunto si mi refracción es, de hecho, refractar o simplemente entrometerme. En su mayor parte, sería más simple mantener el código como está y dejar que Delphi Autocomplete mis funciones con heredado y dejar que Delphi me advierta cuando una función abstracta no se implemente. La única razón por la que lo cambiaría es porque cuando agrego nuevas clases secundarias, estoy copiando y pegando una gran sección de interfaz (que no hace más que volverse más grande) –

7

Depende del problema, como siempre. Yo uso interfaces para definir la interfaz de usuario para el conjunto de clases. Al menos cuando sé que tendré más de una implementación de la clase real subyacente. Por ejemplo, puede tener algo como esto:

IAllInterfaced = interface(IInterface) 
    procedure ImplementMeEverywhere_1(const Params: TParams); 
    procedure ImplementMeEverywhere_2(const Params: TParams); 
    procedure ImplementMeEverywhere_3(const Params: TParams); 
    end; 

    TAllInterfaced_ClassA = class(TInterfacedObject, IAllInterfaced) 
    public 
    procedure ImplementMeEverywhere_1(const Params: TParams); 
    procedure ImplementMeEverywhere_2(const Params: TParams); 
    procedure ImplementMeEverywhere_3(const Params: TParams); 
    end; 

    TAllInterfaced_ClassB = class(TInterfacedObject, IAllInterfaced) 
    public 
    procedure ImplementMeEverywhere_1(const Params: TParams); 
    procedure ImplementMeEverywhere_2(const Params: TParams); 
    procedure ImplementMeEverywhere_3(const Params: TParams); 
    end; 

Aquí no tiene un antepasado común. Cada clase solo implementa la interfaz y no tiene una estructura subyacente común en forma de una clase base común. Esto es posible si las implementaciones son tan diferentes que no comparten nada, pero sí la interfaz. Aún necesita usar la misma interfaz para que sea coherente con los usuarios de las clases derivadas.

La segunda opción es:

IAllAbstract = interface(IInterface) 
    procedure ImplementMeEverywhere_1(const Params: TParams); 
    procedure ImplementMeEverywhere_2(const Params: TParams); 
    procedure ImplementMeEverywhere_3(const Params: TParams); 
    end; 

    TAllAbstract_Custom = (TInterfacedObject, IAllAbstract) 
    private 
    ... 
    public 
    procedure ImplementMeEverywhere_1(const Params: TParams); virtual; abstract; 
    procedure ImplementMeEverywhere_2(const Params: TParams); virtual; abstract; 
    procedure ImplementMeEverywhere_3(const Params: TParams); virtual; abstract; 
    end; 

    TAllAbstract_ClassA = class(TAllAbstract_Custom) 
    public 
    procedure ImplementMeEverywhere_1(const Params: TParams); override; 
    procedure ImplementMeEverywhere_2(const Params: TParams); override; 
    procedure ImplementMeEverywhere_3(const Params: TParams); override; 
    end; 

    TAllAbstract_ClassB = class(TAllAbstract_Custom) 
    public 
    procedure ImplementMeEverywhere_1(const Params: TParams); override; 
    procedure ImplementMeEverywhere_2(const Params: TParams); override; 
    procedure ImplementMeEverywhere_3(const Params: TParams); override; 
    end; 

Aquí tienen una clase base para todas las clases. En esa clase puede tener propiedades comunes o eventos de otras clases, etc. ... Pero todos los procedimientos están marcados como abstractos porque no realizan ninguna tarea común. Abstract garantiza que se implementarán en las clases derivadas, pero no es necesario implementar "FieldA" en todas las clases, solo lo implementa en "TAllAbstract_Custom". Esto asegura que se usa el principio DRY.

La última opción es:

IAllVirtual = interface(IInterface) 
    procedure ImplementMeEverywhere_1(const Params: TParams); 
    procedure ImplementMeEverywhere_2(const Params: TParams); 
    procedure ImplementMeEverywhere_3(const Params: TParams); 
    end; 

    TAllVirtual_Custom = (TInterfacedObject, IAllVirtual) 
    private 
    ... 
    public 
    procedure ImplementMeEverywhere_1(const Params: TParams); virtual; 
    procedure ImplementMeEverywhere_2(const Params: TParams); virtual; 
    procedure ImplementMeEverywhere_3(const Params: TParams); virtual; 
    end; 

    TAllVirtual_ClassA = class(TAllVirtual_Custom) 
    public 
    procedure ImplementMeEverywhere_1(const Params: TParams); override; 
    procedure ImplementMeEverywhere_2(const Params: TParams); override; 
    procedure ImplementMeEverywhere_3(const Params: TParams); override; 
    end; 

    TAllVirtual_ClassB = class(TAllVirtual_Custom) 
    public 
    procedure ImplementMeEverywhere_1(const Params: TParams); override; 
    procedure ImplementMeEverywhere_2(const Params: TParams); override; 
    procedure ImplementMeEverywhere_3(const Params: TParams); override; 
    end; 

Aquí todas las clases derivadas tienen un procedimiento virtual de base común. Esto garantiza que no tenga que implementar todos los procedimientos en el nivel de las clases derivadas. Solo puede anular algunas partes del código o ninguna.

Naturalmente, estos son todos los casos de borde, hay espacio entre ellos. Puedes tener una mezcla de esos conceptos.

Sólo recuerde:

  1. interfaces son poderosa herramienta para garantizar que se oculta la aplicación por parte del usuario y que tiene un punto de uso común (interfaz). También obligan a algunas normas a ser utilizadas, porque la interfaz debe implementarse en su totalidad.
  2. El resumen es una buena herramienta, por lo que no tiene que usar los talones vacíos para los procedimientos que no los necesitan. Por otro lado, te obligan a implementarlos en clases derivadas.
  3. Virtual es útil cuando se tiene un código común que debe implementarse en todas las clases y que garantiza el principio limpio OP y DRY. También son bienvenidos cuando tienes procedimientos que no todas las clases derivadas tienen o necesitan.

Perdón por una respuesta larga, pero no pude dar una explicación fácil aquí porque no la hay. Todo depende del problema en cuestión. Es un equilibrio entre cuánto tienen en común las clases derivadas y cuán diferentes son sus implementaciones.

1

Sí, pode su código.

Hace que su otro código sea mucho más fácil de leer (como ya lo mencionó, sería más fácil ver qué métodos se sobrescriben realmente). Como beneficio adicional, será más fácil cambiar la firma del método en la clase principal: imagine que decide pasar un parámetro más a un método virtual; Realiza el cambio a la clase principal, luego tendrá que repetir el mismo cambio para cada clase secundaria que herede de la clase principal dada. ¡En ese momento no querrás métodos falsos sobreescritos que simplemente llamen "heredados"!

Cuestiones relacionadas