Newsgroup: it.comp.lang.delphi
Titolo: Gestioni delle classi ?

Ciao a tutti,
Un quesito che credo sia banale per chi ha piu eserienza e un livello 
migliore di conoscenza rispetto al mio. Non so come spiegarmi e quindi 
lo faro con un esempio.
Supponiamo di voler realizzare una applicazione che controlla, per 
esempio, l'accensione e lo spegnimento delle luci di una casa.
Supponiamo di voler usare la porta parallela per fare questo, e di aver 
gia pronta una classe che, per esempio, ci permetta di scrivere una 
istruzione del tipo:
parallela.bit2 := true;
e che questa istruzione abiliti il bit 2 della porta dati della parallela.
Ora, tornando alla nostra applicazone che gestisce la casa, e non 
volendo ricordare che a livello hardware la luce della cucina è 
collegata al nostro bit 2, ma volendo scrivere una classe, Tstanza, 
configurabile in modo che sia possibile creare "cucina" e che poi sia 
possibile scrivere una cosa del tipo:
cucina := Tstanza.create(la luce è bit2);
cucina.luceon;
sleep(5000);
cucina.luceoff;
Come si dovrebbe procedere ?
Spero di essermi spiegato abbastanza bene e che il mio quesito sia di 
interesse generale.
ZioGian

 Come si dovrebbe procedere ?
primo modo (triviale)
=====================
type
  TStanza = class
  private
    FLuce: Boolean;
    procedure SetLuce(const Value: Boolean);
  public
    property Luce: Boolean read FLuce write SetLuce;
  end;
procedure TStanza.SetLuce(const Value: Boolean);
begin
  FLuce := Value;
  if FLuce then
    //accendi la luce
  else
    //spegni la luce
end;  
procedure Test;
begin
  Stanza := TStanza.Create;
  try
    Stanza.Luce := True;
    //qualcos'altro
  finally
    Stanza.Free
  end
end;
secondo modo (piů flessibile)
=============================
type
  IStanza = interface
    ['{5CBB7BFA-2F45-4B6E-B950-EC0A2C642F12}']  
//ottieni un GUID premendo CTRL + SHIFT + G
    function GetLuce: Boolean;
    procedure SetLuce(const Value: Boolean);
    property Luce: Boolean read GetLuce write SetLuce;
  end;
  TStanza = class(TInterfacedObject, IStanza)
  private
    FLuce: Boolean;
    function GetLuce: Boolean;
    procedure SetLuce(const Value: Boolean);
  end;
{ TStanza }
function TStanza.GetLuce: Boolean;
begin
  Result := FLuce
end;
procedure TStanza.SetLuce(const Value: Boolean);
begin
  FLuce := Value;
  if FLuce then
    //accendi la luce
  else
    //spegni la luce
end;
procedure Test;
var
  StanzaIntf: IStanza;
  Stanza: TStanza;
begin
  Stanza := TStanza.Create;
  try
    if Stanza.GetInterface(IStanza, StanzaIntf) then
      StanzaIntf.Luce := True;
  finally
    Stanza.Free
  end
end;
-- 
Mauro Falzari 
 
 "Se guardi nell'abisso, anche l'abisso guarda in te." 
   (Friedrich Nietzsche)

 primo modo (triviale)..
Mi accorgo che evidentemente non sono stato molto chiaro. Tornando al 
mio esempio ovviamente vorrei avere anche altre stanze oltre alla 
"cucina", il bagno, la camera etc...
E vorrei scrivere la classe Tstanza una volta sola, e creare le altre 
"stanze" dicendo loro che per esempio il b2 è collegato alla luce della 
cucina, il b1 a quello della camera etc...
E vorrei poter pilotare le varie luci con comandi del tipo:
camera.luce := true;
oppure
camera.luceon (fa poca differenza se accedo alla cosa come proprieta o 
come metodo)
Ovviamente, però, devo poter configurare in quanclhe modo "camera" per 
usare il bit1, "cucina" per il bit2 etc...
Un po come se all'inteno dell'istanza cucina, della classe Tstanza, ci 
fosse un puntatore alla proprieta bitX della parallela.
ZioGian

 camera.luce := true;
cosě puň andar bene?
type
  IImpiantoElettrico = interface
    ['{5CBB7BFA-2F45-4B6E-B950-EC0A2C642F12}']
//ottieni un GUID premendo CTRL + SHIFT + G
    function GetLuce: Boolean;
    procedure SetLuce(const Value: Boolean);
    property Luce: Boolean read GetLuce write SetLuce;
  end;
  TStanzaBase = class(TInterfacedObject, IImpiantoElettrico)
  private
    FLuce: Boolean;
    function GetLuce: Boolean;
    procedure SetLuce(const Value: Boolean); virtual;
  end;
  TBagno = class(TStanzaBase)
  private
    procedure SetLuce(const Value: Boolean); override;
  end;
  TCucina = class(TStanzaBase)
  private
    procedure SetLuce(const Value: Boolean); override;
  end;
{ TStanzaBase }
function TStanzaBase.GetLuce: Boolean;
begin
  Result := FLuce
end;
procedure TStanzaBase.SetLuce(const Value: Boolean);
begin
  FLuce := Value;
  if FLuce then
    //accendi la luce
  else
    //spegni la luce
end;
{ TBagno }
procedure TBagno.SetLuce(const Value: Boolean);
begin
  inherited;
  //setto il bit1 della parallela
end;
{ TCucina }
procedure TCucina.SetLuce(const Value: Boolean);
begin
  inherited;
  //setto il bit2 della parallela
end;
procedure ImpostaLuce(Stanza: TStanzaBase; Value: Boolean);
var
  ImpiantoElettricoIntf: IImpiantoElettrico;
begin
  if Assigned(Stanza) and Stanza.GetInterface(IImpiantoElettrico, 
ImpiantoElettricoIntf) then
    ImpiantoElettricoIntf.Luce := True;
end;
procedure Test;
var
  Bagno: TBagno;
  Cucina: TCucina;
begin
  Bagno := TBagno.Create;
  try
    ImpostaLuce(Bagno, True);
  finally
    Bagno.Free
  end;
  Cucina := TCucina.Create;
  try
    ImpostaLuce(Cucina, True);
  finally
    Cucina.Free
  end
end;
oppure se vuoi stare sul piů semplice, cosě:
============================================
type
  TStanzaBase = class
  private
    FLuce: Boolean;
    procedure SetLuce(const Value: Boolean); virtual;
  public
    property Luce: Boolean read FLuce write SetLuce;
  end;
  TCucina = class(TStanzaBase)
  private
    procedure SetLuce(const Value: Boolean); override;
  end;
  TBagno = class(TStanzaBase)
  private
    procedure SetLuce(const Value: Boolean); override;
  end;
{ TStanzaBase }
procedure TStanzaBase.SetLuce(const Value: Boolean);
begin
  FLuce := Value;
end;
{ TBagno }
procedure TBagno.SetLuce(const Value: Boolean);
begin
  inherited;
  //setto il bit1 della parallela
end;
{ TCucina }
procedure TCucina.SetLuce(const Value: Boolean);
begin
  inherited;
  //setto il bit2 della parallela


 
In primis, ti ringrazio molto per l'interessamento, non certo dovuto.
In secundis, secondo me il modo di gestire la cosa non è il massimo, in 
questa parte in particolare:
 > procedure TBagno.SetLuce(const Value: Boolean);
 > begin
 >   inherited;
 >   //setto il bit1 della parallela
 > end;
 >
 > { TCucina }
 >
 > procedure TCucina.SetLuce(const Value: Boolean);
 > begin
 >   inherited;
 >   //setto il bit2 della parallela
 > end;
Sarei costretto a scrivere una sottoclasse. A mio parere sarebbe meglio 
avere X istanze della classe Tstanza, configurabili per usare un bit a 
piacere.
Questo bit a mio parere dovrebbe essere "assegnato" nella fase 
"oncreate" della classe. In pratica, io creo l'istanza cucina della 
classe Tstanza, e gli assegno il bit che mi piace...
Non voglio creare una classe Tcucina, dove dire che la luce della cucina 
è collegata allo specifico Bit, altrimenti dovrei creare 1 classe per 
ogni stanza della casa. (Questo perche ovviamente l'esempio è un po 
semplificato, in realta le cose da fare sono qualcuna d+ e ogni stanza 
ha 7 o 8 bit differenti..insomma..l'esempio è solo un esempio).
ZioGian

 classe Tstanza, e gli assegno il bit che mi piace...
boh, più di questo non so che dirti:
type
  TTipoStanza = (tsCucina, tsBagno, tsCamera1, tsCamera2);
  TMascheraByte = array [1..8] of Byte;
  TStanza = class
  private
    FTipoStanza: TTipoStanza;
    FTTipoStanza: TTipoStanza;
    FMascheraByte: TMascheraByte;
  public
    constructor Create(ATipoStanza: TTipoStanza);
    property TipoStanza: TTipoStanza read FTTipoStanza;
  end;
{ TStanza }
constructor TStanza.Create(ATipoStanza: TTipoStanza);
var
  i: Integer;
begin
  inherited Create;
  FTipoStanza := ATipoStanza;
//non so se sia necessario inizializzare l'array, ma
//a scanso di trovarsi della memoria "sporca", meglio farlo
  for i := Low(FMascheraByte) to High(FMascheraByte) do
    FMascheraByte[i] := 0;
  case FTipoStanza of
    tsCucina:   FMascheraByte[1] := 1;  //00000001
    tsBagno:    FMascheraByte[2] := 1;  //00000010
    tsCamera1:  FMascheraByte[3] := 1;  //00000100
    tsCamera2:  FMascheraByte[4] := 1;  //00001000
  end;


ZioGian ha scritto:
[cut]
1. una classe TInterruttore che definisce la configurazione (bit, ecc.) 
di uno specifico interruttore
2. una classe TStanza che, tra le eventuali altre proprietą, espone una 
collezione di interruttori
3. una classe TCasa che, tra le eventuali altre proprietą, espone una 
collezione di stanze
Tutta la configurazione delle stanze, degli specifici interruttori per 
stanza e delle caratteristiche di ogni interruttori dovrebbe essere 
archiviata
All'avvio dell'applicazione, una classe TConfigurationProvider si occupa di:
- leggere da database tutte le stanze e le mette in collezione
- per ogni stanza in collezione legge i relativi interruttori e li mette 
nelle specifiche collezioni
Una classe generica TInterruttoreManager che si occupa di comunicare a 
/basso livello/ con la porta seriale/parallela che espone come minimo 
due metodi:
- Accendi(i: TInterruttore);
- Spegni(i: TInterruttore);
che leggendo le caratteristiche dell'interruttore creano le stringhe da 
inviare in porta.
Ciao,
Gianluca.

   TTipoStanza = (tsCucina, tsBagno, tsCamera1, tsCamera2);
In teoria non si dovrebbe mai fare una cosa del genere, perché si butta
via una caratteristica dell'OOP abbastanza utile, che è il polimorfismo:
unit Unit1;
interface
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls,
Forms,
  Dialogs, StdCtrls, Buttons;
type
  TBitValue = 0..1;
  TBitIndex = 0..7;
  TSerialPortSingleton = class
  public
    procedure SetBit(ABitIndex: TBitIndex; ABitValue: TBitValue);
    class function NewInstance: TObject; override;
    procedure FreeInstance; override;
    class function RefCount: Integer;
  end;
  TLightSwitch = class
  private
    FSerialPort: TSerialPortSingleton;
  public
    procedure Turn(const AValue: Boolean); virtual; abstract;
    constructor Create;
    destructor Destroy; override;
  end;
  TRoom = class
  private
    FLightSwitch: TLightSwitch;
    FLight: Boolean;
  protected
    procedure SetLight(const Value: Boolean);
  public
    property Light: Boolean read FLight write SetLight;
    constructor Create; virtual; abstract;
    destructor Destroy; override;
  end;
  TKitchenLightSwitch = class(TLightSwitch)
  public
    procedure Turn(const AValue: Boolean); override;
  end;
  TKitchen = class(TRoom)
  public
    constructor Create; override;
  end;
  TDiningRoomSwitch = class(TLightSwitch)
  public
    procedure Turn(const AValue: Boolean); override;
  end;
  TDiningRoom = class(TRoom)
  public
    constructor Create; override;
  end;
  //
---------------------------------------------------------------------------
  TForm1 = class(TForm)
    BitBtn1: TBitBtn;
    procedure BitBtn1Click(Sender: TObject);
  private
  public
  end;
var
  Form1: TForm1;
implementation
{$R *.dfm}
var
  SerialPort: TSerialPortSingleton = nil;
  Ref_Count: Integer = 0;
{ TSerialPortSingleton }
class function TSerialPortSingleton.NewInstance: TObject;
begin
  if (not Assigned(SerialPort)) then
    SerialPort := TSerialPortSingleton(inherited NewInstance);
  Result := SerialPort;
  Inc(Ref_Count);
end;
procedure TSerialPortSingleton.FreeInstance;
begin
  Dec(Ref_Count);
  if (Ref_Count = 0) then
  begin
    SerialPort.Free;
    SerialPort := nil;
    inherited FreeInstance;
  end;
end;
class function TSerialPortSingleton.RefCount: Integer;
begin
  Result := Ref_Count;
end;
procedure TSerialPortSingleton.SetBit(ABitIndex: TBitIndex; ABitValue:
TBitValue);
begin
  ShowMessageFmt(ClassName+'.SetBit(%d, %d)', [ABitIndex, ABitValue]);
end;
{ TDiningRoomSwitch }
procedure TDiningRoomSwitch.Turn(const AValue: Boolean);
begin
  case AValue of
    True: FSerialPort.SetBit(1, 1);
    False: FSerialPort.SetBit(1, 0);
  end;
end;
{ TKitchenLightSwitch }
procedure TKitchenLightSwitch.Turn(const AValue: Boolean);
begin
  case AValue of
    True: FSerialPort.SetBit(2, 1);
    False: FSerialPort.SetBit(2, 0);
  end;
end;
{ TRoom }
destructor TRoom.Destroy;
begin
  FLightSwitch.Free;
  inherited;
end;
procedure TRoom.SetLight(const Value: Boolean);
begin
  FLight := Value;
  FLightSwitch.Turn(Value);
end;
{ TKitchen }
constructor TKitchen.Create;
begin
  FLightSwitch := TKitchenLightSwitch.Create;
end;
{ TDiningRoom }
constructor TDiningRoom.Create;
begin
  FLightSwitch := TDiningRoomSwitch.Create;
end;
{ TLightSwitch }
constructor TLightSwitch.Create;
begin
  FSerialPort := TSerialPortSingleton.Create;
end;
destructor TLightSwitch.Destroy;
begin
  FSerialPort.Free;
  inherited;
end;
procedure TForm1.BitBtn1Click(Sender: TObject);
begin
  with TKitchen.Create do
    try
      Light := True;
      Light := False;
    finally
      Free;
    end;
  with TDiningRoom.Create do
    try
      Light := True;
      Light := False;
    finally
      Free;
    end;
end;
end.
-- 
questo articolo e` stato inviato via web dal servizio gratuito 
http://www.newsland.it/news
 segnala gli abusi ad 
ab...@newsland.it

 piacere.
"Meglio" da quale punto di vista?
Per un caso cosě semplice, le alternative possibili sono tante, ma il
"metodo" č importante in OOP, specie per affrontare casi complessi.  Da un
certo punto di vista, avere una gerarchia di classi specializzate č
sicuramente meglio che non discriminare a priori quale classe istanziare.


 
Secondo me, per il mio caso, l'approccio di avere una sola classe 
configurabile è piu pratico, perche come dicevo le cose non sono cos' 
semplici come specificavo nell'esempio (che era appunto un esempio, 
utile a spiegarmi).
In realta', dovrei realizzare una apparecchiatura per il collaudo di una 
scheda da noi prodotta. Utilizzando una scheda I/O.
Tempo fa ho gia scritto delle semplici classi per la gestione della 
scheda IO, che mi permettono di pilotare il singolo bit della singola porta.
Le schede che vorrei testare,  contemporaneamente, sono 6.
Quindi, per me sarebbe comodo scrivere una classe TTest, che contenga un 
po tutto quello che mi serve per eseguire una sequenza di test della 
scheda.
Ovviamente, ogni scheda lavora con bit differenti della scheda di IO, da 
  cui la mia "necessita" di poter configurtare la classe ipotetica TTest 
nel momento in cui viene istanziata.
Mi scuso per avere forse sempilifaco un po troppo le cose non il mio 
esempio.
ZioGian

 
[cut]
Grazie andrea per l'aiuto, credo che in 4 o 5 anni riusciro a capire il 
tuo esempio..
^___^
Scherzi a parte..sei un po troppo avanti per le mie assai modeste capacità.
ZioGian

 
....
[cut]
Grazie per la dritta, credo che tenterň un aproccio del genere.
ZioGian