Difference between revisions of "Understanding Interfaces/es"

From Lazarus wiki
Jump to navigationJump to search
m (Bart moved page Understanding Interfaces to Understanding Interfaces/es without leaving a redirect: Pages without language suffix are supposed to be in English)
(Clean up all the article, in a more "Spanish Traditional (Castillian)" language.)
 
(2 intermediate revisions by one other user not shown)
Line 1: Line 1:
 +
{{Understanding interfaces}}
  
== La razón de las interfaces
+
== La razón de las interfaces ==
  
----
+
Las clases que extienden otras clases pueden llamarse sub-clases o clases derivadas.  Por ejemplo, podría extenderse la clase <code>bicicleta</code> para tener una sub-clase <code>montañera</code>, y una sub-clase <code>paseo</code>.  Estas clases heredan muchas de las funciones comunes de la clase <code>bicicleta</code>, pero añaden opciones únicas como la baca para la de <code>paseo</code>.  Puedes llamar métodos de <code>bicicleta</code>, sabiendo que se aplican generalmente a todos los tipos de bicicleta.
  
 +
Este es un uso tradicional y estándar para las clases, dado que las sub-clases son solamente variaciones sobre un tema. Pero suponiendo que hubiera una aplicación donde existieran objetos de clase bicicleta, de automóviles, de lavacoches y otros, y en dicha aplicación se quisiera que cada clase tuviera el siguiente método:
  
Las clases que extienden otras clases pueden llamarse sub-clases.  Por ejemplo, podría extenderse la clase bicicleta para tener una sub-clase montañera, y una sub-clase monareta.  Ellas heredan muchas de las funciones comunes de la clase bicicleta., pero se agregan opciones únicas como ruedas estabilizadoras para la monareta.  Puedes llamar métodos de bicicleta, sabiendo que aplican generalmente a todos los tipos de bicicleta.
+
<syntaxhighlight lang="pascal">
 +
function EsReciclable : Boolean;
 +
</syntaxhighlight>
  
Este es un uso tradicional y estándar para las clases, desde que las sub-clases son sólo variaciones de un tema. Pero suponiendo que tuvieras una aplicación donde tuvieras objetos de clase bicicleta, de carros, de lava-autos y otrosY en esta aplicación quisieras que cada clase tuviera el siguiente método:
+
Esto permitiría saber si el objeto contiene materiales reciclables.  Se podría definir una clase de alto nivel que contuviera este método, y definir todas las clases derivadas de ella.  O simplemente podrías agregar el método a cada sub-clase. O podrían usarse ''interfaces''.  Las interfaces simplemente pondrían orden en esta situación. Definiendo una interfaz que agrupe estos métodos juntos, establece una norma para todas las clases que implementen la interfazHacen más fácil que el programador se discipline en desarrollar la aplicación; todas las clases tienen un conjunto de métodos que son idénticos y el compilador insiste que todos los métodos en una interfaz se implementen.
  
 '''function''' EsReciclable : '''Boolean''';
+
Regresando al escenario del objeto reciclable, cada sub-clase hereda funciones comunes de su clase padre en particular.  Esto describe la estructura básica de la sub-clase.  También hereda (por implementación) las funciones comunes que se extienden por todas las clases.
  
Esto le dejaría saber si el objeto contiene materiales reciclables.  Podríase definir una clase de alto nivel que contuviera este método, y definir todas las clases derivadas de ella.  O simplemente podrías agregar el método a cada sub-clase.
 
  
O podría usar interfaces.  Las interfaces simplemente pondría en orden esta situación. Definiendo una interfaz que agrupe estos métodos en común, coloca un estándar para todas las clases que implementen la interfaz.  Hacen más fácil que el programador se discipline en desarrollar la aplicación; todas las clases tienen un conjunto de métodos que son idénticos y el compilador insiste que todos los métodos en una interfaz se implementen.
+
== Ejemplo de interfaz ==
  
Regresando al escenario del objeto reciclable, cada sub-clase hereda funciones comunes de su clase padre en particular.  Esto describe la estructura básica de la sub-claseTambién hereda (por implementación) funciones comunes que atraviesan a todas las clases.
+
Como muchas ideas, un ejemplo es la mejor forma de ilustrar los conceptosLo haremos por etapas para que las cosas sean tan simples como sea posible. Primero, definamos una clase automóvil:
  
 +
<syntaxhighlight lang="pascal">
 +
type
 +
(* Definimos nuestra clase Automóvil. *)
 +
  TAutomovil = class
 +
  private
 +
    fMarca: String;
 +
    fEdad: Byte;
 +
  public
 +
  (* Constructor del Automóvil. *)
 +
    constructor Create (aMarca: String);
 +
  published
 +
  (* Propiedades del Automóvil. *)
 +
    property Marca: String read fMarca;
 +
    property Edad: Byte read fEdad write fEdad;
 +
  end;
 +
</syntaxhighlight>
  
 +
Esta clase se define por defecto basado en [[TObject]] dado que no especificamos el tipo de clase base.  Esta clase tiene dos propiedades, y un constructor, que se muestran aquí:
  
== Ejemplo de una interfaz
+
<syntaxhighlight lang="pascal">
 
+
(* Constructor de la implementación para la clase Automóvil. *)
 
+
  constructor TAutomovil.Create (aMarca : String);
Como muchas ideas, un ejemplo es la mejor forma de ilustrar los conceptos.  Lo haremos por etapas para mantener las cosas tan simples como sea posible.  Primero, definamos una clase carro:
+
  begin
 
+
  (* Guarda el marca del automóvil y pone la edad por defecto. *)
 type
+
    fMarca := aMarca;
   // Definimos nuestra clase Carro
+
    fEdad := 0
   TCarro = class
+
  end;
   private
+
</syntaxhighlight>
     carroMarca: String;
 
     carroEdad: Byte;
 
   public
 
     // Constructor del Carro
 
     constructor Create(marca: String);
 
   published
 
     // Propiedades del Carro
 
     property Marca: String read carroMarca;
 
     property Edad: Byte read carroEdad write carroEdad;
 
   end;
 
 
 
Esta clase se define por defecto basado en TObject desde que no especificamos un tipo de clase base.  Esta clase tiene dos propiedades, y un constructor, que se muestran aquí:
 
 
 
 // Constructor de la implementación para la clase Carro
 
 constructor TCarro.Create(marca : String);
 
 begin
 
   // Guarda el marca del carro y pone por defecto la edad
 
   carroMarca := marca;
 
   carroEdad := 0;
 
 end;
 
 
 
 
 
Aquí asignamos las dos propiedades del carro: su marca y edad.  La marca se pasa como parámetro y asignamos una edad por defecto de 0.  Ahora definiremos otra clase – una clase bicicleta:
 
 
 
 type
 
   // Definimos nuestra clase Bicicleta
 
   TBicicleta = class
 
   private
 
     ciclaEsMacho: Boolean;
 
     ciclaTamRueda: Byte;
 
   published
 
     // Propiedades de la Bicicleta
 
     property esMacho: Boolean read ciclaEsMacho;
 
     property tamRueda: Byte read ciclaTamRueda write ciclaTamRueda;
 
     // Constructor de la Bicicleta
 
     constructor Create(esMacho: Boolean; tamRueda: Byte);
 
   end;
 
  
 +
Aquí asignamos las dos propiedades del automóvil: su marca y edad.  La marca se pasa como parámetro y asignamos una edad por defecto de 0.  Ahora definiremos otra clase – una clase bicicleta:
  
 +
<syntaxhighlight lang="pascal">
 +
type
 +
(* Definimos nuestra clase Bicicleta. *)
 +
  TBicicleta = class
 +
  private
 +
    fFemenina: Boolean;
 +
    fEdad: Byte;
 +
  public
 +
  (* Constructor de la Bicicleta. *)
 +
    constructor Create (aFemenina: Boolean; aEdad: Byte);
 +
  published
 +
  (* Propiedades de la Bicicleta. *)
 +
    property esFemenina: Boolean read fFemenina;
 +
    property Edad: Byte read fEdad write fEdad;
 +
  end;
 +
</syntaxhighlight>
  
 
Esta clase tiene dos propiedades y un constructor como aquí se muestra:
 
Esta clase tiene dos propiedades y un constructor como aquí se muestra:
  
 // Constructor de la implementación para la clase bicicleta
+
<syntaxhighlight lang="pascal">
 constructor TBicicleta.Create(esMacho: Boolean; tamRueda: Byte);
+
(* Constructor de la implementación para la clase bicicleta. *)
 begin
+
  constructor TBicicleta.Create (aFemenina: Boolean; aEdad: Byte);
   // Save the passed parameters
+
  begin
   ciclaEsMacho := esMacho;
+
  (* Save the passed parameters. *)
   ciclaTamRueda := tamRueda;
+
    fFemenina := aFemenina;
 end;
+
    fEdad := aEdad
 +
  end;
 +
</syntaxhighlight>
  
 +
Esta clase es un poco distinta de la clase automóvil, lo suficiente para ver que podríamos no haber basado el automóvil en la bicicleta o la bicicleta en el automóvil.  Ahora definiremos una interfaz que diga si un clase de objeto es reciclable:
  
Esta clase es un poco distinta de la clase del carro, suficiente para ver que podríamos no haber basado el carro en la bicicleta o la bicicleta en el carro. Ahora definiremos una interfaz que diga si un objeto clase es reciclable:
+
<syntaxhighlight lang="pascal">
 +
  type
 +
  (* Una definición de interfaz. *)
 +
    IReciclable = Interface (IInterface)
 +
    (* Una sola función que da soporte a la propiedad. *)
 +
      function MaterialEsReciclable: Boolean;
 +
    (* Una sola propiedad. *)
 +
      property esReciclable: Boolean read MaterialEsReciclable;
 +
    end;
 +
</syntaxhighlight>
  
 type
+
Nuestra interfaz usa la definición estándar IInterface como base.  Las definiciones de interfaces son como definiciones de clase con todos los elementos abstractos, así que no tenemos que declararlos como abstractos – lo son por defecto.
   // Una definición de interfaz
 
   IReciclable = Interface(IInterface)
 
     // Una sola función que da soporte a la propiedad
 
     function MiraSiEsReciclable: Boolean;
 
     // Una sola propiedad
 
     property esReciclable: Boolean read MiraSiEsReciclable;
 
   end;
 
  
 +
Esta interfaz añade una propiedad esReciclable a cada clase que la implemente.  Para cada clase que la implemente se garantizará que tendrá exactamente la misma forma de preguntar si es reciclable.  Este es el poder y el beneficio de las interfaces – uniformidad a través de clases potencialmente muy distintas.
  
Nuestra interfaz usa la definición estándar Iinterface como baseLas definiciones de interfaces son como definiciones de clase con todos los elementos abstractos, así que no tenemos que declararlos como abstractos – por defecto lo son.
+
Cualquier clase puede implementar tantas interfaces como se quiera – puede conformar cualquier estándar global en efectoNótese que debemos definir la propiedad usando una función – no podemos declarar un campo de datos <code>Boolean</code> en la interrfaz dado que las interfaces no contienen datos.
  
Esta interfaz añade una propiedad esReciclable a cada clase que la implemente.  Cada clase que la implementa tendrá que ser garantizada para tener exactamente la misma forma de preguntar si es reciclable.  Este es el poder y el beneficio de las interfaces – uniformidad a través de clases potencialmente muy distintas.
+
Ahora cambiemos nuestras clases para que den soporte a esta definición de interfaz:
  
Cualquier clase puede implementar tantas interfaces como se quiera – puede conformar cualquier estándar global en efecto.
+
<syntaxhighlight lang="pascal">
 +
  type
 +
  (* Definimos nuestra clase Automóvil. *)
 +
    TAutomovil = class (TInterfacedObject, IReciclable)
 +
    private
 +
      fMarca: String;
 +
      fEdad: Byte;
 +
      fEsReciclable: Boolean; { Añadido para dar soporte a IReciclable. }
 +
      function MaterialEsReciclable: Boolean; { Añadido para IReciclable. }
 +
    public
 +
    (* Constructor del Automóvil. *)
 +
      constructor Create (aMarca: String);
 +
    published
 +
    (* Propiedades del Automóvil. *)
 +
      property Marca: String read fMarca;
 +
      property Edad: Byte read fEdad write fEdad;
 +
    (* Añadido para IReciclable. *)
 +
      property esReciclable: Boolean read MaterialEsReciclable;
 +
    end;
 +
</syntaxhighlight>
  
Note que debemos definir la propiedad como usando una función – no podemos declarar un campo de datos Booleano en la interrfaz mientras las interfaces no contengan datos.
+
Nótese que pusimos la función usada por la propiedad de interfaz <code>esReciclable</code> en la sección privada (<code>private</code>) – queremos que la propiedad la use sólo quien la llame.
  
Ahora cambiemos nuestras clases para dar soporte a esta definición de interfaz:
+
[Nota del autor: cuando se compila, el compilador insiste en la presencia de la función <code>MaterialEsReciclable</code>, pero no en la parte más importante – la propiedad.  Así como lo puede ver el autor, esto no obliga la opción predominante de la interfaz – ¡la propiedad!]
  
 type
+
Ahora hemos basado nuestra clase en la clase <code>[[TInterfaceObject]]</code>, la cual provee de algo de soporte estándar para las clases que implementan interfaces.  Y hemos también basado nuestra clase en <code>IReciclable</code>, nuestra nueva interfaz.
   // Definimos nuestra clase Carro
 
   TCarro = class(TInterfacedObject, IReciclable)
 
   private
 
     carroMarca: String;
 
     carroEdad: Byte;
 
     carroEsReciclable: Boolean; // Añadido para dar soporte a IReciclable
 
     function MiraSiEsReciclable : Boolean; // Añadido para IReciclable
 
   public
 
     // Constructor del Carro
 
     constructor Create(marca: String);
 
   published
 
     // Propiedades del Carro
 
     property Marca: String read carroMarca;
 
     property Edad: Byte read carroEdad write carroEdad;
 
     // Añadido para IReciclable
 
     property esReciclable: Boolean read MiraSiEsReciclable;
 
   end;
 
  
 +
Pero también debemos declarar la nueva función <code>MaterialEsReciclable</code>:
  
Note que pusimos la función usada por la propiedad de interfaz esReciclable en la sección privada – queremos que use la propiedad sólo quien le llama.
+
<syntaxhighlight lang="pascal">
 +
(* Función del automóvil requerida por el atributo EsReciclable. *)
 +
  function TAutomovil.MaterialEsReciclable: Boolean;
 +
  begin
 +
    Result := fEsReciclable
 +
  end;
 +
</syntaxhighlight>
  
(Nota del autor: cuando se compila, el compilador insiste en la presencia de la función MiraSiEsReciclable, pero no en la parte más importante – la propiedad.  Así como lo puede ver el autor, esto no obliga la opción predominante de la interfaz – la propiedad!).
+
Y no debemos olvidar asignar este valor reciclable. Lo haremos diréctamente aquí en el constructor:
  
Ahora hemos basado nuestra clase en la clase TInterfaceObject, la cual provee algo de soporte estándar para las clases que implementan interfaces.  Y hemos también basado nuestra clase en IReciclable, nuestra nueva interfaz.
+
<syntaxhighlight lang="pascal">
 +
(* Constructor para la clase automóvil. *)
 +
  constructor TAutomovil.Create (aMarca: String);
 +
  begin
 +
  (* Guarda la marca del automóvil y asigna una edad por defecto. *)
 +
    fMarca := aMarca;
 +
    fEdad := 0;
 +
    fEsReciclable := true { Asigna que es reciclable. }
 +
  end;
 +
</syntaxhighlight>
  
Pero también debemos declarar la nueva función MiraSiEsReciclable:
+
¡Vaya!  Pero debemos hacer lo mismo para la clase <code>Bicicleta</code> para mostrar el auténtico efecto.  Veremos el código completo para definir y usar estas clases, las cuales puedes copiar y pegar en el ''Editor de Código'':
  
 // Función del Carro requerida por el atributo EsReciclable
+
<syntaxhighlight lang="pascal">
 function TCarro.MiraSiEsReciclable: Boolean;
+
unit Unit1;
 begin
 
   Result := carroEsReciclable;
 
 end;
 
  
Y no debemos olvidar asignar este valor reciclable. Lo haremos crudamente aquí en el constructor:
+
{$mode objfpc}{$H+}
  
 // Constructor implmentation for the car class
+
interface
 constructor TCarro.Create(marca: String);
 
 begin
 
   // Guarda la marca del carro y asigna una edad por defecto
 
   carroNombre := marca;
 
   carroEdad := 0;
 
   carroEsReciclable := true; // Asigna que es reciclable
 
 end;
 
  
Uff! Pero debemos hacer lo mismo para la clase Bicicleta para mostrar el efecto real.  Veremos el código completo para definir y usar estas clases, las cuales puedes copiar y pegar en el Editor de Código:
+
  uses
 +
    Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs;
  
   '''unit''' Unit1;
+
   type
 +
  (* Una definición de interfaz. *)
 +
    IReciclable = Interface (IInterface)
 +
    (* Una sola función que da soporte a la propiedad. *)
 +
      function MaterialEsReciclable: Boolean;
 +
    (* Una sola propiedad. *)
 +
      property esReciclable: Boolean read MaterialEsReciclable;
 +
    end;
  
   {$mode objfpc}{$H+}
+
   (* Definimos nuestra clase Automóvil. *)
 +
    TAutomovil = class (TInterfacedObject, IReciclable)
 +
    private
 +
      fMarca: String;
 +
      fEdad: Byte;
 +
      fEsReciclable: Boolean; { Añadido para dar soporte a IReciclable. }
 +
      function MaterialEsReciclable: Boolean; { Añadido para IReciclable. }
 +
    public
 +
    (* Constructor del Automóvil. *)
 +
      constructor Create (aMarca: String);
 +
    published
 +
    (* Propiedades del Automóvil. *)
 +
      property Marca: String read fMarca;
 +
      property Edad: Byte read fEdad write fEdad;
 +
    (* Añadido para IReciclable. *)
 +
      property esReciclable: Boolean read MaterialEsReciclable;
 +
    end;
  
   '''interface'''
+
   (* Definimos nuestra clase Bicicleta. *)
 +
    TBicicleta = class (TInterfacedObject, IReciclable)
 +
    private
 +
      fFemenina: Boolean;
 +
      fEdad: Byte;
 +
      function MaterialEsReciclable: Boolean; { Añadido para IReciclable. }
 +
    public
 +
    (* Constructor de la Bicicleta. *)
 +
      constructor Create (aFemenina: Boolean; aEdad: Byte);
 +
    published
 +
    (* Propiedades de la Bicicleta. *)
 +
      property esFemenina: Boolean read fFemenina;
 +
      property Edad: Byte read fEdad write fEdad;
 +
    (* Añadido para IReciclable. *)
 +
      property esReciclable: Boolean read MaterialEsReciclable;
 +
    end;
  
   '''uses'''
+
{ TForm1 }
     Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs;
+
  TForm1 = class (TForm)
 +
    procedure FormCreate (Sender: TObject);
 +
   private
 +
     { private declarations }
 +
  public
 +
    { public declarations }
 +
  end;
  
   '''type'''
+
   var
     // Una definición de interfaz
+
     Form1: TForm1;
    IReciclable = '''Interface'''(IInterface)
 
      // Una sola función que da soporte a la propiedad
 
      '''function''' MiraSiEsReciclable : Boolean;
 
      // Una sola propiedad
 
      '''property''' esReciclable : '''Boolean''' '''read''' MiraSiEsReciclable;
 
    '''end''';
 
  
    // Definimos nuestra clase Carro
+
implementation
    TCarro = '''class'''(TInterfacedObject, IReciclable)
 
    '''private'''
 
        carroMarca: '''String''';
 
        carroEdad: '''Byte''';
 
        carroEsReciclable: '''Boolean''';
 
        '''function''' MiraSiEsReciclable: '''Boolean'''; // Agregado para IReciclable
 
    '''public'''
 
        // Constructor del Carro
 
        '''constructor''' Create(marca: String);
 
    '''published'''
 
        // Propiedades del Carro
 
        '''property''' Marca: '''String''' '''read''' carroMarca;
 
        '''property''' Edad: '''Byte read''' carroEdad '''write''' carroEdad;
 
        // Agregado para IReciclable
 
        '''property''' esReciclable: '''Boolean read''' MiraSiEsReciclable;
 
    '''end''';
 
  
    // Definimos nuestra clase Bicicleta
+
{$R *.frm}
    TBicicleta = '''class'''(TInterfacedObject, IReciclable)
 
    '''private'''
 
        ciclaEsMacho: '''Boolean''';
 
        ciclaTamRueda: '''Byte''';
 
        function MiraSiEsReciclable: '''Boolean'''; // Agregado para IReciclable
 
    '''public'''
 
        // Constructor de la Bicicleta
 
        '''constructor''' Create(esMacho: '''Boolean'''; tamRueda: Byte);
 
    '''published'''
 
        // Propiedades de la Bicicleta
 
        '''property''' esMacho: '''Boolean''' '''read''' ciclaEsMacho;
 
        '''property''' tamRueda: '''Byte''' '''read''' ciclaTamRueda '''write''' ciclaTamRueda;
 
        // Agregado para IReciclable
 
        '''property''' esReciclable : '''Boolean''' '''read''' MiraSiEsReciclable;
 
    '''end''';
 
  
  { TForm1 }
+
{ TForm1 }
  
   TForm1 = '''class'''(TForm)
+
   procedure TForm1.FormCreate (Sender: TObject);
     '''procedure''' FormCreate(Sender: '''TObject''');
+
  var
     '''private'''
+
     biciMama: TBicicleta;
      { private declarations }
+
    cochePapa: TAutomovil;
     '''public'''
+
  begin
       { public declarations }
+
  { Instancia los objetos bicicleta y automóvil. }
    '''end''';
+
    biciMama := TBicicleta.Create (true, 36);
 +
     cochePapa := TAutomovil.Create ('Toyota Hilux');
 +
  { Pregunta a cada si cada uno si es reciclable. }
 +
     if cochePapa.esReciclable then
 +
      ShowMessage ('El coche de papá es reciclable.')
 +
    else
 +
      ShowMessage ('El coche de papá no es reciclable.');
 +
    if biciMama.esReciclable then
 +
      ShowMessage ('La bici de mamá es reciclable.')
 +
    else
 +
       ShowMessage ('La bici de mamá no es reciclable.')
 +
  end;
  
  '''var'''
 
    Form1: TForm1;
 
  
  '''implementation'''
 
  
   {$R *.frm}
+
(* Constructor para la clase automóvil. *)
 +
  constructor TAutomovil.Create (aMarca: String);
 +
   begin
 +
  (* Guarda la marca del automóvil y asigna una edad por defecto. *)
 +
    fMarca := aMarca;
 +
    fEdad := 0;
 +
    fEsReciclable := true { Asigna que es reciclable. }
 +
  end;
  
   { TForm1 }
+
(* Función del Automóvil requerida para el atributo esReciclable. *)
 +
   function TAutomovil.MaterialEsReciclable: Boolean;
 +
  begin
 +
    Result := fEsReciclable
 +
  end;
  
  '''procedure''' TForm1.FormCreate(Sender: TObject);
 
  '''var'''
 
    ciclaMama: TBicicleta;
 
    carroPapa: TCarro;
 
  '''begin'''
 
    // Instanciar nuestros objetos bicicleta y carro
 
    ciclaMama := TBicicleta.Create(false, 36);
 
    carroPapa := TCarro.Create('Toyota Hilux');
 
    // Preguntar si cada uno es reciclable
 
    if carroPapa.esReciclable then
 
        ShowMessage('El carro de papá es reciclable')
 
    else ShowMessage('El carro de papá no es reciclable');
 
    if ciclaMama.esReciclable then
 
        ShowMessage('La cicla de mamá es reciclable')
 
    else ShowMessage('La cicla de mamá no es reciclable');
 
  '''end''';
 
  
    //Constructor de la implementación para la clase Carro
 
  '''constructor''' TCarro.Create(marca: '''String''');
 
  '''begin'''
 
    // Guarda la marca del Carro y asigna la edad por defecto
 
    carroMarca := marca;
 
    carroEdad := 0;
 
    carroEsReciclable := true // Asigna que es reciclable
 
  '''end''';
 
  
    //Función del Carro requerida para el atributo EsReciclable
+
(* Constructor de la implementación para la clase bicicleta. *)
   '''function''' TCarro.MiraSiEsReciclable: Boolean;
+
   constructor TBicicleta.Create (aFemenina: Boolean; aEdad: Byte);
   '''begin'''
+
   begin
     Result := carroEsReciclable;
+
  (* Save the passed parameters. *)
   '''end''';
+
     fFemenina := aFemenina;
 +
    fEdad := aEdad
 +
   end;
  
    //Constructor de la implementación para la clase bicicleta
+
(* Función de la Bicicleta requerida para el atributo esReciclable. *)
   '''constructor''' TBicicleta.Create(esMacho: '''Boolean'''; tamRueda: '''Byte''');
+
   function TBicicleta.MaterialEsReciclable: Boolean;
 
   begin
 
   begin
    // Guarda los parámetros que se pasaron
+
  { Vamos a presuponer que sólo las bicibletas para hombre son reciclables. }
    ciclaEsMacho := esMacho;
+
     Result := not self.esFemenina
     ciclaTamRueda := tamRueda;
 
 
   end;
 
   end;
  
    // Función de la bicicleta requerida para EsReciclable
+
end.
  '''function''' TBicicleta.MiraSiEsReciclable: '''Boolean''';
+
</syntaxhighlight>
  '''begin'''
+
 
    // Asumiremos que sólo las bicicletas Macho son reciclables
+
Al ejecutar... [[ShowMessage]] muestra lo siguiente:
    Result := self.esMacho;
+
 
  '''end''';
+
El coche de papá es reciclable.
  end.
+
  La bici de mamá no es reciclable.
  
<nowiki>Al ejecutar... se muestra lo siguiente:</nowiki>
 
  
 La función ShowMessage muestra lo siguiente:
+
Este post fue tomado de un artículo de internet que me pareció bien interesante para modificar y entender las interfaces.  Siempre ha sido un tema tabú(?) preguntar y cuando se usan, puede darle más claridad a tu código.
 El carro de papá es reciclable
 
 La cicla de mamá no es reciclable
 
  
 +
== Ver también ==
  
Este post fue tomado de un artículo de internet que me pareció bien interesante para modificar y entender las interfaces.  Siempre ha sido un tema tabú de preguntar y cuando se usan, puede darle más claridad a tu código.
+
*[[Accessing the Interfaces directly]]
 +
*[[How To Use Interfaces]]
 +
*[[Interfaces]]

Latest revision as of 20:02, 15 March 2020

English (en) español (es)

La razón de las interfaces

Las clases que extienden otras clases pueden llamarse sub-clases o clases derivadas. Por ejemplo, podría extenderse la clase bicicleta para tener una sub-clase montañera, y una sub-clase paseo. Estas clases heredan muchas de las funciones comunes de la clase bicicleta, pero añaden opciones únicas como la baca para la de paseo. Puedes llamar métodos de bicicleta, sabiendo que se aplican generalmente a todos los tipos de bicicleta.

Este es un uso tradicional y estándar para las clases, dado que las sub-clases son solamente variaciones sobre un tema. Pero suponiendo que hubiera una aplicación donde existieran objetos de clase bicicleta, de automóviles, de lavacoches y otros, y en dicha aplicación se quisiera que cada clase tuviera el siguiente método:

function EsReciclable : Boolean;

Esto permitiría saber si el objeto contiene materiales reciclables. Se podría definir una clase de alto nivel que contuviera este método, y definir todas las clases derivadas de ella. O simplemente podrías agregar el método a cada sub-clase. O podrían usarse interfaces. Las interfaces simplemente pondrían orden en esta situación. Definiendo una interfaz que agrupe estos métodos juntos, establece una norma para todas las clases que implementen la interfaz. Hacen más fácil que el programador se discipline en desarrollar la aplicación; todas las clases tienen un conjunto de métodos que son idénticos y el compilador insiste que todos los métodos en una interfaz se implementen.

Regresando al escenario del objeto reciclable, cada sub-clase hereda funciones comunes de su clase padre en particular. Esto describe la estructura básica de la sub-clase. También hereda (por implementación) las funciones comunes que se extienden por todas las clases.


Ejemplo de interfaz

Como muchas ideas, un ejemplo es la mejor forma de ilustrar los conceptos. Lo haremos por etapas para que las cosas sean tan simples como sea posible. Primero, definamos una clase automóvil:

type
(* Definimos nuestra clase Automóvil. *)
  TAutomovil = class
  private
    fMarca: String;
    fEdad: Byte;
  public
  (* Constructor del Automóvil. *)
    constructor Create (aMarca: String);
  published
  (* Propiedades del Automóvil. *)
    property Marca: String read fMarca;
    property Edad: Byte read fEdad write fEdad;
  end;

Esta clase se define por defecto basado en TObject dado que no especificamos el tipo de clase base. Esta clase tiene dos propiedades, y un constructor, que se muestran aquí:

(* Constructor de la implementación para la clase Automóvil. *)
  constructor TAutomovil.Create (aMarca : String);
  begin
  (* Guarda el marca del automóvil y pone la edad por defecto. *)
    fMarca := aMarca;
    fEdad := 0
  end;

Aquí asignamos las dos propiedades del automóvil: su marca y edad. La marca se pasa como parámetro y asignamos una edad por defecto de 0. Ahora definiremos otra clase – una clase bicicleta:

type
(* Definimos nuestra clase Bicicleta. *)
  TBicicleta = class
  private
    fFemenina: Boolean;
    fEdad: Byte;
  public
  (* Constructor de la Bicicleta. *)
    constructor Create (aFemenina: Boolean; aEdad: Byte);
  published
  (* Propiedades de la Bicicleta. *)
    property esFemenina: Boolean read fFemenina;
    property Edad: Byte read fEdad write fEdad;
  end;

Esta clase tiene dos propiedades y un constructor como aquí se muestra:

(* Constructor de la implementación para la clase bicicleta. *)
  constructor TBicicleta.Create (aFemenina: Boolean; aEdad: Byte);
  begin
  (* Save the passed parameters. *)
    fFemenina := aFemenina;
    fEdad := aEdad
  end;

Esta clase es un poco distinta de la clase automóvil, lo suficiente para ver que podríamos no haber basado el automóvil en la bicicleta o la bicicleta en el automóvil. Ahora definiremos una interfaz que diga si un clase de objeto es reciclable:

  type
  (* Una definición de interfaz. *)
    IReciclable = Interface (IInterface)
    (* Una sola función que da soporte a la propiedad. *)
      function MaterialEsReciclable: Boolean;
    (* Una sola propiedad. *)
      property esReciclable: Boolean read MaterialEsReciclable;
    end;

Nuestra interfaz usa la definición estándar IInterface como base. Las definiciones de interfaces son como definiciones de clase con todos los elementos abstractos, así que no tenemos que declararlos como abstractos – lo son por defecto.

Esta interfaz añade una propiedad esReciclable a cada clase que la implemente. Para cada clase que la implemente se garantizará que tendrá exactamente la misma forma de preguntar si es reciclable. Este es el poder y el beneficio de las interfaces – uniformidad a través de clases potencialmente muy distintas.

Cualquier clase puede implementar tantas interfaces como se quiera – puede conformar cualquier estándar global en efecto. Nótese que debemos definir la propiedad usando una función – no podemos declarar un campo de datos Boolean en la interrfaz dado que las interfaces no contienen datos.

Ahora cambiemos nuestras clases para que den soporte a esta definición de interfaz:

  type
  (* Definimos nuestra clase Automóvil. *)
    TAutomovil = class (TInterfacedObject, IReciclable)
    private
      fMarca: String;
      fEdad: Byte;
      fEsReciclable: Boolean; { Añadido para dar soporte a IReciclable. }
      function MaterialEsReciclable: Boolean; { Añadido para IReciclable. }
    public
    (* Constructor del Automóvil. *)
      constructor Create (aMarca: String);
    published
    (* Propiedades del Automóvil. *)
      property Marca: String read fMarca;
      property Edad: Byte read fEdad write fEdad;
    (* Añadido para IReciclable. *)
      property esReciclable: Boolean read MaterialEsReciclable;
    end;

Nótese que pusimos la función usada por la propiedad de interfaz esReciclable en la sección privada (private) – queremos que la propiedad la use sólo quien la llame.

[Nota del autor: cuando se compila, el compilador insiste en la presencia de la función MaterialEsReciclable, pero no en la parte más importante – la propiedad. Así como lo puede ver el autor, esto no obliga la opción predominante de la interfaz – ¡la propiedad!]

Ahora hemos basado nuestra clase en la clase TInterfaceObject, la cual provee de algo de soporte estándar para las clases que implementan interfaces. Y hemos también basado nuestra clase en IReciclable, nuestra nueva interfaz.

Pero también debemos declarar la nueva función MaterialEsReciclable:

(* Función del automóvil requerida por el atributo EsReciclable. *)
  function TAutomovil.MaterialEsReciclable: Boolean;
  begin
    Result := fEsReciclable
  end;

Y no debemos olvidar asignar este valor reciclable. Lo haremos diréctamente aquí en el constructor:

(* Constructor para la clase automóvil. *)
  constructor TAutomovil.Create (aMarca: String);
  begin
  (* Guarda la marca del automóvil y asigna una edad por defecto. *)
    fMarca := aMarca;
    fEdad := 0;
    fEsReciclable := true { Asigna que es reciclable. }
  end;

¡Vaya! Pero debemos hacer lo mismo para la clase Bicicleta para mostrar el auténtico efecto. Veremos el código completo para definir y usar estas clases, las cuales puedes copiar y pegar en el Editor de Código:

unit Unit1;

{$mode objfpc}{$H+}

interface

  uses
    Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs;

  type
  (* Una definición de interfaz. *)
    IReciclable = Interface (IInterface)
    (* Una sola función que da soporte a la propiedad. *)
      function MaterialEsReciclable: Boolean;
    (* Una sola propiedad. *)
      property esReciclable: Boolean read MaterialEsReciclable;
    end;

  (* Definimos nuestra clase Automóvil. *)
    TAutomovil = class (TInterfacedObject, IReciclable)
    private
      fMarca: String;
      fEdad: Byte;
      fEsReciclable: Boolean; { Añadido para dar soporte a IReciclable. }
      function MaterialEsReciclable: Boolean; { Añadido para IReciclable. }
    public
    (* Constructor del Automóvil. *)
      constructor Create (aMarca: String);
    published
    (* Propiedades del Automóvil. *)
      property Marca: String read fMarca;
      property Edad: Byte read fEdad write fEdad;
    (* Añadido para IReciclable. *)
      property esReciclable: Boolean read MaterialEsReciclable;
    end;

  (* Definimos nuestra clase Bicicleta. *)
    TBicicleta = class (TInterfacedObject, IReciclable)
    private
      fFemenina: Boolean;
      fEdad: Byte;
      function MaterialEsReciclable: Boolean; { Añadido para IReciclable. }
    public
    (* Constructor de la Bicicleta. *)
      constructor Create (aFemenina: Boolean; aEdad: Byte);
    published
    (* Propiedades de la Bicicleta. *)
      property esFemenina: Boolean read fFemenina;
      property Edad: Byte read fEdad write fEdad;
    (* Añadido para IReciclable. *)
      property esReciclable: Boolean read MaterialEsReciclable;
    end;

{ TForm1 }
  TForm1 = class (TForm)
    procedure FormCreate (Sender: TObject);
  private 
    { private declarations }
  public
    { public declarations }
  end;

  var
    Form1: TForm1;

implementation

{$R *.frm}

{ TForm1 }

  procedure TForm1.FormCreate (Sender: TObject);
  var
    biciMama: TBicicleta;
    cochePapa: TAutomovil;
  begin
  { Instancia los objetos bicicleta y automóvil. }
    biciMama := TBicicleta.Create (true, 36);
    cochePapa := TAutomovil.Create ('Toyota Hilux');
  { Pregunta a cada si cada uno si es reciclable. }
    if cochePapa.esReciclable then
      ShowMessage ('El coche de papá es reciclable.')
    else
      ShowMessage ('El coche de papá no es reciclable.');
    if biciMama.esReciclable then
      ShowMessage ('La bici de mamá es reciclable.')
    else
      ShowMessage ('La bici de mamá no es reciclable.')
  end;



(* Constructor para la clase automóvil. *)
  constructor TAutomovil.Create (aMarca: String);
  begin
  (* Guarda la marca del automóvil y asigna una edad por defecto. *)
    fMarca := aMarca;
    fEdad := 0;
    fEsReciclable := true { Asigna que es reciclable. }
  end;

(* Función del Automóvil requerida para el atributo esReciclable. *)
  function TAutomovil.MaterialEsReciclable: Boolean;
  begin
    Result := fEsReciclable
  end;



(* Constructor de la implementación para la clase bicicleta. *)
  constructor TBicicleta.Create (aFemenina: Boolean; aEdad: Byte);
  begin
  (* Save the passed parameters. *)
    fFemenina := aFemenina;
    fEdad := aEdad
  end;

(* Función de la Bicicleta requerida para el atributo esReciclable. *)
  function TBicicleta.MaterialEsReciclable: Boolean;
  begin
  { Vamos a presuponer que sólo las bicibletas para hombre son reciclables. }
    Result := not self.esFemenina
  end;

end.

Al ejecutar... ShowMessage muestra lo siguiente:

El coche de papá es reciclable.
La bici de mamá no es reciclable.


Este post fue tomado de un artículo de internet que me pareció bien interesante para modificar y entender las interfaces. Siempre ha sido un tema tabú(?) preguntar y cuando se usan, puede darle más claridad a tu código.

Ver también