<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Le blog de erca57 &#187; Prog. Orientée Objets</title>
	<atom:link href="https://blog.developpez.com/erca57/pcategory/prog-orientee-objets/feed" rel="self" type="application/rss+xml" />
	<link>https://blog.developpez.com/erca57</link>
	<description></description>
	<lastBuildDate>Fri, 28 Aug 2009 07:38:11 +0000</lastBuildDate>
	<language>fr-FR</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.1.42</generator>
	<item>
		<title>Persistance des objets par SQL : créer une base</title>
		<link>https://blog.developpez.com/erca57/p7168/prog-orientee-objets/persistance_des_objets_par_sql_creer_une</link>
		<comments>https://blog.developpez.com/erca57/p7168/prog-orientee-objets/persistance_des_objets_par_sql_creer_une#comments</comments>
		<pubDate>Thu, 05 Feb 2009 10:17:49 +0000</pubDate>
		<dc:creator><![CDATA[erca57]]></dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Delphi win32]]></category>
		<category><![CDATA[Persistance]]></category>
		<category><![CDATA[Prog. Orientée Objets]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[16e pisode 2.2.6 &#8211; Créer une nouvelle base de données Nous pouvons maintenant gérer les personnes dans une table PEOPLE. Ce que nous ne pouvons pas faire, c&#8217;est créer un nouveau fichier de base de données de la même façon que nous avons créé de nouveaux fichiers-textes. Nous allons donc rajouter cette fonctionnalité à notre application. procedure TFormRepert.aNewExecute(Sender: TObject); begin &#160; if not(ExtractFileExt(CurrentFileName) = '.fdb') then &#160; if (MessageDlg('Voulez-vous enregistrer le fichier courant ?', &#160; [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>16e pisode</p>
<p><strong>2.2.6 &#8211; Créer une nouvelle base de données</strong></p>
<p>Nous pouvons maintenant gérer les personnes dans une table PEOPLE. Ce que nous ne pouvons pas faire, c&rsquo;est créer un nouveau fichier de base de données de la même façon que nous avons créé  de nouveaux fichiers-textes. Nous allons donc rajouter cette fonctionnalité à notre application.</p>
<p><span id="more-16"></span></p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">procedure TFormRepert.aNewExecute(Sender: TObject); <br />
begin <br />
&nbsp; if not(ExtractFileExt(CurrentFileName) = '.fdb') then <br />
&nbsp; if (MessageDlg('Voulez-vous enregistrer le fichier courant ?', <br />
&nbsp; mtWarning, [mbYes, mbNo], 0) = mrYes) <br />
&nbsp; then <br />
&nbsp; &nbsp; aSaveExecute(Sender); <br />
&nbsp; People.Clear; <br />
&nbsp; ListBox1.Clear; <br />
&nbsp; SaveDialog1.InitialDir := ExtractFilePath(CurrentFileName); <br />
&nbsp; if SaveDialog1.Execute then <br />
&nbsp; &nbsp; CurrentFileName := SaveDialog1.FileName; <br />
&nbsp; if ExtractFileExt(CurrentFileName) = '.fdb' then begin <br />
&nbsp; &nbsp; Visitor := TSQLCreatePeople.Create; <br />
&nbsp; &nbsp; try <br />
&nbsp; &nbsp; &nbsp; People.AcceptBizObjVisitor(Visitor); <br />
&nbsp; &nbsp; finally <br />
&nbsp; &nbsp; &nbsp; Visitor.Free; <br />
&nbsp; &nbsp; end; <br />
&nbsp; &nbsp; Self.Caption := ExtractFileName(CurrentFileName); <br />
&nbsp; &nbsp; aSave.Enabled := False; <br />
&nbsp; &nbsp; aSaveAs.Enabled := False; <br />
&nbsp; end; <br />
end;</div></div>
<p>Si à partir de l&rsquo;interface nous demandons à créer un nouveau fichier, nous devrons maintenant définir le nom du fichier. Si ce fichier est un fichier <em>Firebird</em> (extension .fdb), nous appliquons le visiteur <em>TSQLCreatePeople</em> à <em>People</em>, ce qui a pour effet de créer une nouvelle base de données dans sa méthode <em>Create</em>. Nous prenons soin d&rsquo;afficher le nouveau nom de fichier dans l&rsquo;interface et nous désactivons les actions de sauvegarde.</p>
<p>Pour que la nouvelle base soit créée, nous modifions la méthode <em>TSQLFirebird.Create</em> comme suit: </p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">constructor TSQLFirebird.Create(var aFileName: TFileName); <br />
begin <br />
&nbsp; // aFileName must have .fdb extension <br />
&nbsp; if ExtractFileExt(aFileName) &lt;&gt; '.fdb' then <br />
&nbsp; &nbsp; ChangeFileExt(aFileName, '.fdb'); <br />
&nbsp; FDatabaseName := aFileName; <br />
&nbsp; inherited Create(aFileName); <br />
&nbsp; //connexion à la base de données <br />
&nbsp; FDB := TIBDatabase.Create(nil); <br />
&nbsp; with FDB do <br />
&nbsp; begin <br />
&nbsp; &nbsp; DatabaseName := FDatabaseName; <br />
&nbsp; &nbsp; Params.Clear; <br />
&nbsp; &nbsp; if FileExists(FDatabaseName) then <br />
&nbsp; &nbsp; &nbsp; begin <br />
&nbsp; &nbsp; &nbsp; Params.Add('user_name=SYSDBA'); <br />
&nbsp; &nbsp; &nbsp; Params.Add('password=cm5z2p3r'); <br />
&nbsp; &nbsp; &nbsp; end <br />
&nbsp; &nbsp; else begin <br />
&nbsp; &nbsp; &nbsp; Params.Add('USER ''SYSDBA'''); <br />
&nbsp; &nbsp; &nbsp; Params.Add('PASSWORD ''cm5z2p3r'''); <br />
&nbsp; &nbsp; &nbsp; Params.Add('PAGE_SIZE 4096'); <br />
&nbsp; &nbsp; &nbsp; FDB.CreateDatabase; <br />
&nbsp; &nbsp; end; <br />
&nbsp; &nbsp; LoginPrompt := False; <br />
&nbsp; &nbsp; Connected := True; <br />
&nbsp; end; <br />
&nbsp; ...//la suite est inchangée <br />
&nbsp;<br />
end;</div></div>
<p>Ensuite le visiteur se charge de créer la table PEOPLE. Il nous suffit pour cela de définir la méthode <em>Init</em> puis d&rsquo;appeler la méthode <em>VisitBizObj</em> de l&rsquo;ancêtre <em>TSQLCreate</em>.</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">{ <br />
******************************* TSQLCreatePeople ******************************* <br />
} <br />
procedure TSQLCreatePeople.Init; <br />
begin <br />
&nbsp; FQuery.SQL.Clear; <br />
&nbsp; FQuery.SQL.Add('CREATE TABLE PEOPLE ('); <br />
&nbsp; FQuery.SQL.Add('OID INTEGER,'); <br />
&nbsp; FQuery.SQL.Add('LastName VARCHAR(20),'); <br />
&nbsp; FQuery.SQL.Add('FirstName VARCHAR(20),'); <br />
&nbsp; FQuery.SQL.Add('BirthDate DATE);'); <br />
end; <br />
&nbsp;<br />
procedure TSQLCreatePeople.SetupParams; <br />
begin <br />
&nbsp; //no params <br />
end; <br />
&nbsp;<br />
procedure TSQLCreatePeople.VisitBizObj(Visited: TBizObj); <br />
begin <br />
&nbsp; if not(Visited is TPeople) then <br />
&nbsp; &nbsp; Exit; <br />
&nbsp; inherited VisitBizObj(Visited); <br />
end; <br />
&nbsp;<br />
&nbsp;<br />
{ <br />
********************************** TSQLCreate ********************************** <br />
} <br />
procedure TSQLCreate.VisitBizObj(Visited: TBizObj); <br />
begin <br />
&nbsp; inherited VisitBizObj(Visited); <br />
&nbsp; Init; // Set the SQL. Implemented in the concrete class <br />
&nbsp; FQuery.ExecSQL; <br />
end;</div></div>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Persistance des objets par SQL : suppression</title>
		<link>https://blog.developpez.com/erca57/p7166/prog-orientee-objets/persistance_des_objets_par_sql_suppressi</link>
		<comments>https://blog.developpez.com/erca57/p7166/prog-orientee-objets/persistance_des_objets_par_sql_suppressi#comments</comments>
		<pubDate>Wed, 04 Feb 2009 10:01:37 +0000</pubDate>
		<dc:creator><![CDATA[erca57]]></dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Delphi win32]]></category>
		<category><![CDATA[Persistance]]></category>
		<category><![CDATA[Prog. Orientée Objets]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[14e épisode 2.2.4 &#8211; Visiteur TSQLDeletePerson Pour effacer un objet TPerson de la base de données, nous créons le visiteur TSQLDeletePerson. Il est construit sur le même modèle que TSQLUpdatePerson et ne fait que redéfinir les méthodes Init et SetupParams: { ******************************* TSQLDeletePerson ******************************* } procedure TSQLDeletePerson.Init; begin &#160; FQuery.SQL.Clear; &#160; FQuery.SQL.Add('DELETE FROM PEOPLE'); &#160; FQuery.SQL.Add('WHERE OID = :OID;'); end; &#160; procedure TSQLDeletePerson.SetupParams; var &#160; IData: TPerson; begin &#160; IData := TPerson(FVisited); &#160; FQuery.Params.ParamByName('OID').AsInteger := [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>14e épisode</p>
<p><strong>2.2.4 &#8211; Visiteur TSQLDeletePerson</strong></p>
<p>Pour effacer un objet <em>TPerson</em> de la base de données, nous créons le visiteur <strong>TSQLDeletePerson</strong>. Il est construit sur le même modèle que <em>TSQLUpdatePerson</em> et ne fait que redéfinir les méthodes <em>Init</em> et <em>SetupParams</em>:</p>
<p><span id="more-14"></span></p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">{ <br />
******************************* TSQLDeletePerson ******************************* <br />
} <br />
procedure TSQLDeletePerson.Init; <br />
begin <br />
&nbsp; FQuery.SQL.Clear; <br />
&nbsp; FQuery.SQL.Add('DELETE FROM PEOPLE'); <br />
&nbsp; FQuery.SQL.Add('WHERE OID = :OID;'); <br />
end; <br />
&nbsp;<br />
procedure TSQLDeletePerson.SetupParams; <br />
var <br />
&nbsp; IData: TPerson; <br />
begin <br />
&nbsp; IData := TPerson(FVisited); <br />
&nbsp; FQuery.Params.ParamByName('OID').AsInteger := IData.OID; <br />
end;</div></div>
<p>Le visiteur est appliqué à l&rsquo;objet <em>TPerson</em> à effaçer comme suit:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">procedure TFormRepert.aDeleteExecute(Sender: TObject); <br />
var <br />
&nbsp; aPerson: TPerson; <br />
&nbsp; Visitor: TSQLDeletePerson; <br />
begin <br />
&nbsp; if ListBox1.ItemIndex &lt; 0 then begin <br />
&nbsp; &nbsp; MessageDlg('Sélectionnez une ligne', mtError, [mbOK], 0); <br />
&nbsp; &nbsp; Exit; <br />
&nbsp; end; <br />
&nbsp; aPerson := TPerson(People.List.Items[ListBox1.ItemIndex]); <br />
&nbsp; aPerson.State := bosDeleted; <br />
&nbsp; if (MessageDlg('Effacer &quot;'+ListBox1.Items[ListBox1.ItemIndex]+'&quot; ?', <br />
&nbsp; mtConfirmation, [mbYes,mbNo], 0) = mrYes) <br />
&nbsp; then begin <br />
&nbsp; &nbsp; if ExtractFileExt(CurrentFileName) = '.fdb' then begin <br />
&nbsp; &nbsp;aPerson.State := bosDelete; <br />
&nbsp; &nbsp; &nbsp; Visitor := TSQLDeletePerson.Create(CurrentFileName); <br />
&nbsp; &nbsp; &nbsp; try <br />
&nbsp; &nbsp; &nbsp; &nbsp; aPerson.AcceptBizObjVisitor(Visitor); <br />
&nbsp; &nbsp; &nbsp; finally <br />
&nbsp; &nbsp; &nbsp; &nbsp; Visitor.Free; <br />
&nbsp; &nbsp; &nbsp; end; <br />
&nbsp; &nbsp; end; <br />
&nbsp; &nbsp; if aPerson.State = bosDeleted then <br />
&nbsp; &nbsp; &nbsp; People.List.Delete(ListBox1.ItemIndex); <br />
&nbsp; &nbsp; DisplayList; <br />
&nbsp; end; <br />
end;</div></div>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Persistance des objets par SQL : insertion</title>
		<link>https://blog.developpez.com/erca57/p7167/prog-orientee-objets/persistance_des_objets_par_sql_insertion</link>
		<comments>https://blog.developpez.com/erca57/p7167/prog-orientee-objets/persistance_des_objets_par_sql_insertion#comments</comments>
		<pubDate>Wed, 04 Feb 2009 10:14:24 +0000</pubDate>
		<dc:creator><![CDATA[erca57]]></dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Delphi win32]]></category>
		<category><![CDATA[Persistance]]></category>
		<category><![CDATA[Prog. Orientée Objets]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[15e épisode 2.2.5 &#8211; Visiteur TSQLInsertPerson L&#8217;insertion dans la base d&#8217;un nouvel objet TPerson ne différe pas beaucoup de la mise à jour. { ******************************* TSQLInsertPerson ******************************* } procedure TSQLInsertPerson.Init; begin &#160; FQuery.SQL.Clear; &#160; FQuery.SQL.Add('INSERT INTO PEOPLE VALUES ('); &#160; FQuery.SQL.Add(':OID,'); &#160; FQuery.SQL.Add(':LastName,'); &#160; FQuery.SQL.Add(':FirstName,'); &#160; FQuery.SQL.Add(':BirthDate);'); end; &#160; procedure TSQLInsertPerson.SetupParams; var &#160; IData: TPerson; begin &#160; IData := TPerson(FVisited); &#160; FQuery.Params.ParamByName('OID').AsInteger := IData.OID; &#160; FQuery.Params.ParamByName('LastName').AsString := IData.LastName; &#160; FQuery.Params.ParamByName('FirstName').AsString := IData.FirstName; &#160; FQuery.Params.ParamByName('BirthDate').AsDateTime := [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>15e épisode</p>
<p><strong>2.2.5 &#8211; Visiteur TSQLInsertPerson</strong></p>
<p>L&rsquo;insertion dans la base d&rsquo;un nouvel objet <em>TPerson</em> ne différe pas beaucoup de la mise à jour.</p>
<p><span id="more-15"></span></p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">{ <br />
******************************* TSQLInsertPerson ******************************* <br />
} <br />
procedure TSQLInsertPerson.Init; <br />
begin <br />
&nbsp; FQuery.SQL.Clear; <br />
&nbsp; FQuery.SQL.Add('INSERT INTO PEOPLE VALUES ('); <br />
&nbsp; FQuery.SQL.Add(':OID,'); <br />
&nbsp; FQuery.SQL.Add(':LastName,'); <br />
&nbsp; FQuery.SQL.Add(':FirstName,'); <br />
&nbsp; FQuery.SQL.Add(':BirthDate);'); <br />
end; <br />
&nbsp;<br />
procedure TSQLInsertPerson.SetupParams; <br />
var <br />
&nbsp; IData: TPerson; <br />
begin <br />
&nbsp; IData := TPerson(FVisited); <br />
&nbsp; FQuery.Params.ParamByName('OID').AsInteger := IData.OID; <br />
&nbsp; FQuery.Params.ParamByName('LastName').AsString := IData.LastName; <br />
&nbsp; FQuery.Params.ParamByName('FirstName').AsString := IData.FirstName; <br />
&nbsp; FQuery.Params.ParamByName('BirthDate').AsDateTime := IData.BirthDate; <br />
end;</div></div>
<p>Ce nouveau visiteur est appliqué à l&rsquo;objet TPerson comme suit :</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">procedure TFormRepert.aCreateExecute(Sender: TObject); <br />
var <br />
&nbsp; newPerson: TPerson; <br />
&nbsp; Visitor: &nbsp; TSQLInsertPerson; <br />
begin <br />
&nbsp; newPerson := TPerson.Create; <br />
&nbsp; TFormPerson.Execute(newPerson); <br />
&nbsp; if newPerson = nil then <br />
&nbsp; &nbsp; Exit; <br />
&nbsp; if newPerson.Dirty then <br />
&nbsp; &nbsp; begin <br />
&nbsp; &nbsp; if not(ExtractFileExt(CurrentFileName) = '.fdb') then <br />
&nbsp; &nbsp; &nbsp; newPerson.Dirty := False <br />
&nbsp; &nbsp; else begin <br />
&nbsp; &nbsp; &nbsp; Visitor := TSQLInsertPerson.Create(CurrentFileName); <br />
&nbsp; &nbsp; &nbsp; try <br />
&nbsp; &nbsp; &nbsp; &nbsp; newPerson.AcceptBizObjVisitor(Visitor); <br />
&nbsp; &nbsp; &nbsp; finally <br />
&nbsp; &nbsp; &nbsp; &nbsp; Visitor.Free; <br />
&nbsp; &nbsp; &nbsp; end; <br />
&nbsp; &nbsp; end; <br />
&nbsp; &nbsp; if not newPerson.Dirty then <br />
&nbsp; &nbsp; &nbsp; People.Add(newPerson); <br />
&nbsp; &nbsp; DisplayList; <br />
&nbsp; &nbsp; end <br />
&nbsp; else <br />
&nbsp; &nbsp; newPerson.Free; <br />
end;</div></div>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Persistance des objets par SQL : mise à jour</title>
		<link>https://blog.developpez.com/erca57/p7165/prog-orientee-objets/persistance_des_objets_par_sql_mise_a_jo</link>
		<comments>https://blog.developpez.com/erca57/p7165/prog-orientee-objets/persistance_des_objets_par_sql_mise_a_jo#comments</comments>
		<pubDate>Wed, 04 Feb 2009 09:49:56 +0000</pubDate>
		<dc:creator><![CDATA[erca57]]></dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Delphi win32]]></category>
		<category><![CDATA[Persistance]]></category>
		<category><![CDATA[Prog. Orientée Objets]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[13e épisode Nous avons décidé d&#8217;écrire chaque objet de la classe TPerson dans la base au fur et à mesure de sa création, de sa modification ou de sa suppression. Nous pourrions très bien mettre à jour la base de données au moment de quitter l&#8217;application, comme nous l&#8217;avons fait pour les fichiers-textes, mais avec le risque de perdre toutes nos mises à jour en cas de plantage. On accède aux fichiers-textes séquentiellement, ce qui [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>13e épisode</p>
<p>Nous avons décidé d&rsquo;écrire chaque objet de la classe <em>TPerson</em> dans la base au fur et à mesure de sa création, de sa modification ou de sa suppression. Nous pourrions très bien mettre à jour la base de données au moment de quitter l&rsquo;application, comme nous l&rsquo;avons fait pour les fichiers-textes, mais avec le risque de perdre toutes nos mises à jour en cas de plantage. On accède aux fichiers-textes séquentiellement, ce qui ne permet pas la modification, l&rsquo;insertion ou la suppression d&rsquo;une ligne sans réécrire tout le fichier. C&rsquo;est pourquoi nous écrivons les fichiers-textes en une seule fois au moment de quitter l&rsquo;application. Par contre, une base de données SQL est spécialement conçue pour permettre l&rsquo;accès direct à une ligne d&rsquo;une table. Autant en profiter, et effectuer la mise à jour à chaque modification d&rsquo;un objet <em>TPerson</em>.</p>
<p><span id="more-13"></span><br />
<strong>2.2.3 &#8211; Visiteur TSQLUpdatePerson</strong> </p>
<p>Pour permettre la mise à jour de chaque objet <em>TPerson</em>, nous créons une nouvelle hiérarchie de classes.</p>
<p>Pour commencer, nous allons écrire le visiteur chargé de la <strong>mise à jour</strong> d&rsquo;un enregistrement de la table <em>People</em>.</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; TSQLUpdatePerson -&gt; TSQLWritePerson -&gt; TSQLWrite -&gt; TSQLFirebird -&gt; TBizObjVisitor</div></div>
<p>Rappelons que :<br />
&#8211; <em>TBizObjVisitor</em> stocke la référence de l&rsquo;objet appelant (<em>FVisited</em>) et implémente la méthode <em>Finalize</em> qui met à jour l&rsquo;état de l&rsquo;objet;<br />
&#8211; <em>TSQLFirebird</em> gère la connexion à la base dans le constructeur et le destructeur.</p>
<p>Trois nouvelles classes sont apparues:<br />
&#8211; <strong>TSQLWrite</strong> implémente <em>VisitBizObj(Visited:TBizObj)</em> qui exécute les méthodes <em>Init</em> et <em>SetupParams</em> du visiteur dans l&rsquo;ordre adéquat;<br />
&#8211; <strong>TSQLWritePerson</strong> vérifie que l&rsquo;objet visité est bien de la classe <em>TPerson</em> (dans <em>VisitBizObj</em>);<br />
&#8211; <strong>TSQLUpdatePerson</strong> implémente <em>Init</em> et <em>SetupParams</em>.</p>
<p><em>TBizObjVisitor</em> et <em>TSQLFirebird</em> ont déjà été décrits précédemment. Voyons les trois autres:</p>
<p>- <strong>TSQLWrite</strong></p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">procedure TSQLWrite.VisitBizObj(Visited: TBizObj); <br />
begin <br />
&nbsp; inherited VisitBizObj(Visited); <br />
&nbsp; Init; //implémenté dans TSQLUpdatePerson <br />
&nbsp; SetupParams; //implémenté dans TSQLUpdatePerson <br />
&nbsp; FQuery.ExecSQL; <br />
&nbsp; Finalize; // implémenté dans TBizObjVisitor <br />
end;</div></div>
<p>- <strong>TSQLWritePerson</strong></p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">procedure TSQLWritePerson.VisitBizObj(Visited: TBizObj); <br />
begin <br />
&nbsp; if not(Visited is TPerson) then <br />
&nbsp; &nbsp; Exit; <br />
&nbsp; inherited VisitBizObj(Visited); //ci-dessus dans TSQLWrite <br />
end;</div></div>
<p>- <strong>TSQLUpdatePerson</strong></p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">procedure TSQLUpdatePerson.Init; <br />
begin <br />
&nbsp; FQuery.SQL.Clear; <br />
&nbsp; FQuery.SQL.Add('UPDATE PEOPLE SET'); <br />
&nbsp; FQuery.SQL.Add('LastName = :LastName,'); <br />
&nbsp; FQuery.SQL.Add('FirstName = :FirstName,'); <br />
&nbsp; FQuery.SQL.Add('BirthDate = :BirthDate'); <br />
&nbsp; FQuery.SQL.Add('WHERE OID = :OID;'); <br />
end; <br />
&nbsp;<br />
procedure TSQLUpdatePerson.SetupParams; <br />
var <br />
&nbsp; IData: TPerson; <br />
begin <br />
&nbsp; IData := TPerson(FVisited); <br />
&nbsp; FQuery.Params.ParamByName('OID').AsInteger := IData.OID; <br />
&nbsp; FQuery.Params.ParamByName('LastName').AsString := IData.LastName; <br />
&nbsp; FQuery.Params.ParamByName('FirstName').AsString := IData.FirstName; <br />
&nbsp; FQuery.Params.ParamByName('BirthDate').AsDateTime := IData.BirthDate; <br />
end;</div></div>
<p>Le visiteur sera appliqué à l&rsquo;objet <em>TPerson</em> après son édition avec <em>TFormPerson</em> :</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">procedure TFormRepert.aEditExecute(Sender: TObject); <br />
var <br />
&nbsp; aPerson: TPerson; <br />
begin <br />
&nbsp; if ListBox1.ItemIndex &lt; 0 then begin <br />
&nbsp; &nbsp; MessageDlg('Sélectionnez une ligne', mtError, [mbOK], 0); <br />
&nbsp; &nbsp; Exit; <br />
&nbsp; end; <br />
&nbsp; aPerson := TPerson(People.List.Items[ListBox1.ItemIndex]); <br />
&nbsp; TFormPerson.Execute(aPerson); <br />
&nbsp; if ExtractFileExt(CurrentFileName) = '.fdb' then begin <br />
&nbsp; &nbsp; aPerson.AcceptBizObjVisitor(TSQLUpdatePerson.Create(CurrentFileName)); <br />
&nbsp; end; <br />
&nbsp; DisplayList; <br />
end;</div></div>
<p>En fait, au cous de la mise au point de l&rsquo;application, j&rsquo;ai été amené à écrire une version un peu plus sophistiquée de cette méthode :</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">procedure TFormRepert.aEditExecute(Sender: TObject); <br />
var <br />
&nbsp; aPerson: &nbsp; &nbsp;TPerson; <br />
&nbsp; aPersonOld: TPerson; <br />
&nbsp; Visitor: &nbsp; &nbsp;TSQLUpdatePerson; <br />
begin <br />
&nbsp; if ListBox1.ItemIndex &lt; 0 then begin <br />
&nbsp; &nbsp; MessageDlg('Sélectionnez une ligne', mtError, [mbOK], 0); <br />
&nbsp; &nbsp; Exit; <br />
&nbsp; end; <br />
&nbsp; aPerson := TPerson(People.List.Items[ListBox1.ItemIndex]); <br />
&nbsp; aPersonOld := TPerson.Create; <br />
&nbsp; try <br />
&nbsp; &nbsp; aPersonOld.OID := aPerson.OID; <br />
&nbsp; &nbsp; aPersonOld.BirthDate := aPerson.BirthDate; <br />
&nbsp; &nbsp; aPersonOld.FirstName := aPerson.FirstName; <br />
&nbsp; &nbsp; aPersonOld.LastName := aPerson.LastName; <br />
&nbsp; &nbsp; TFormPerson.Execute(aPerson); <br />
&nbsp; &nbsp; if ExtractFileExt(CurrentFileName) = '.fdb' then <br />
&nbsp; &nbsp; if aPerson.Compare(aPersonOld) &gt; 0 then begin <br />
&nbsp; &nbsp; &nbsp; Visitor := TSQLUpdatePerson.Create(CurrentFileName); <br />
&nbsp; &nbsp; &nbsp; try <br />
&nbsp; &nbsp; &nbsp; &nbsp; aPerson.AcceptBizObjVisitor(Visitor); <br />
&nbsp; &nbsp; &nbsp; finally <br />
&nbsp; &nbsp; &nbsp; &nbsp; Visitor.Free; <br />
&nbsp; &nbsp; &nbsp; end; <br />
&nbsp; &nbsp; end; <br />
&nbsp; &nbsp; DisplayList; <br />
&nbsp; finally <br />
&nbsp; &nbsp; aPersonOld.Free; <br />
&nbsp; end; <br />
end;</div></div>
<p>Remarquez la nouvelle méthode <em>Compare</em> qui permet de comparer deux objets <em>TPerson</em>, afin de n&rsquo;effectuer la requête SQL que s&rsquo;il y a eu modification des données. L&rsquo;implémentation de cette méthode dans <em>TPerson</em> est la suivante:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">function TPerson.Compare(aBizObj: TBizObj): Integer; <br />
var <br />
&nbsp; Count: Integer; <br />
begin <br />
&nbsp; Count := 0; <br />
&nbsp; if FBirthDate &lt;&gt; TPerson(aBizObj).BirthDate then <br />
&nbsp; &nbsp; inc(Count); <br />
&nbsp; if FFirstName &lt;&gt; TPerson(aBizObj).FirstName then <br />
&nbsp; &nbsp; inc(Count); <br />
&nbsp; if FLastName &lt;&gt; TPerson(aBizObj).LastName then <br />
&nbsp; &nbsp; inc(Count); <br />
&nbsp; Result := Count; <br />
end;</div></div>
<p>Le résultat est supérieur à 0 si un champ a été modifié.</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Persistance des objets : base de données (suite)</title>
		<link>https://blog.developpez.com/erca57/p7162/prog-orientee-objets/persistance_des_objets_base_de_donnees_s</link>
		<comments>https://blog.developpez.com/erca57/p7162/prog-orientee-objets/persistance_des_objets_base_de_donnees_s#comments</comments>
		<pubDate>Tue, 03 Feb 2009 13:51:19 +0000</pubDate>
		<dc:creator><![CDATA[erca57]]></dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Delphi win32]]></category>
		<category><![CDATA[Persistance]]></category>
		<category><![CDATA[Prog. Orientée Objets]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Ce 12e billet vous montre les modifications à apporter à l&#8217;interface-utilisateur. Le changement de stratégie annoncé en 2.2 consiste à privilégier l&#8217;écriture dans la base de l&#8217;objet &#171;&#160;personne&#160;&#187; à chaque modification, suppression ou création. Cela implique qu&#8217;à la fermeture de l&#8217;application tous les objets sont d&#8217;ores et déjà sauvegardés. Nous allons donc ignorer le sauvegarde en quittant (on close) dans la form TFormRepert quand le fichier courant est une base de données (identifiée ici par [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Ce 12e billet vous montre les modifications à apporter à l&rsquo;interface-utilisateur.</p>
<p><span id="more-12"></span></p>
<p>Le changement de stratégie annoncé en 2.2 consiste à privilégier l&rsquo;écriture dans la base de l&rsquo;objet &laquo;&nbsp;personne&nbsp;&raquo; à chaque modification, suppression ou création. Cela implique qu&rsquo;à la fermeture de l&rsquo;application tous les objets sont d&rsquo;ores et déjà sauvegardés. Nous allons donc ignorer le sauvegarde en quittant (on close) dans la form <em>TFormRepert</em> quand le fichier courant est une base de données (identifiée ici par son extension .fdb) :</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">procedure TFormRepert.FormClose(Sender: TObject; var Action: TCloseAction); <br />
begin <br />
&nbsp; if ExtractFileExt(CurrentFileName) = '.fdb' then <br />
&nbsp; &nbsp; Exit; <br />
&nbsp; if MessageDlg('Sauvegarder ' + CurrentfileName + ' ?', <br />
&nbsp; mtConfirmation, [mbYes, mbNo], 0) = mrYes <br />
&nbsp; then <br />
&nbsp; &nbsp; aSaveExecute(Sender); <br />
end;</div></div>
<p>Pour les mêmes raisons nous invalidons les actions de sauvegarde qui n&rsquo;ont pas de sens ici dans la mesure où la base est à jour au moment où ces commandes sont accessibles dans l&rsquo;interface. Cette invalidation est exécutée à l&rsquo;ouverture du fichier (<em>if ExtractFileExt(CurrentFileName) = &lsquo;.fdb&rsquo;</em>) :</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">procedure TFormRepert.aOpenExecute(Sender: TObject); <br />
begin <br />
&nbsp; if FileExists(CurrentFileName) then begin <br />
&nbsp; &nbsp; if (MessageDlg('Voulez-vous enregistrer le fichier courant'+#13+#10 <br />
&nbsp; &nbsp; +CurrentFileName+' ?', mtWarning, [mbYes, mbNo], 0) = mrYes) <br />
&nbsp; &nbsp; then <br />
&nbsp; &nbsp; &nbsp; aSaveExecute(Sender); <br />
&nbsp; &nbsp; OpenDialog1.InitialDir := ExtractFilePath(CurrentFileName); <br />
&nbsp; end; <br />
&nbsp; if OpenDialog1.Execute then begin <br />
&nbsp; &nbsp; CurrentFileName := OpenDialog1.FileName; <br />
&nbsp; &nbsp; ReadPeopleFromFile(CurrentFileName); <br />
&nbsp; &nbsp; DisplayList; <br />
&nbsp; end; <br />
&nbsp; Self.Caption := ExtractFileName(CurrentFileName); <br />
&nbsp; if ExtractFileExt(CurrentFileName) = '.fdb' then <br />
&nbsp; &nbsp; begin <br />
&nbsp; &nbsp; aSave.Enabled := False; <br />
&nbsp; &nbsp; aSaveAs.Enabled := False; <br />
&nbsp; &nbsp; end <br />
&nbsp; else begin <br />
&nbsp; &nbsp; aSave.Enabled := True; <br />
&nbsp; &nbsp; aSaveAs.Enabled := True; <br />
&nbsp; end; <br />
end;</div></div>
<p>Profitons-en, maintenant que nous disposons de fichiers de tests, pour modifier la méthode <em>FormCreate</em> en enlevant les instructions de création d&rsquo;objets de test :</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">procedure TFormRepert.FormCreate(Sender: TObject); <br />
begin <br />
&nbsp; if not FileExists(CurrentFileName) then <br />
&nbsp; &nbsp; aOpenExecute(Sender) <br />
&nbsp; else begin <br />
&nbsp; &nbsp; ReadPeopleFromFile(CurrentFileName); <br />
&nbsp; &nbsp; Self.Caption := ExtractFileName(CurrentFileName); <br />
&nbsp; &nbsp; DisplayList; <br />
&nbsp; end; <br />
end;</div></div>
<p>Au lancement de l&rsquo;application, il vous sera demandé de sélectionner le fichier à ouvrir. Si vous voulez que le dernier fichier utilisé s&rsquo;ouvre par défaut, il faudra sauvegarder <em>CurrentfileName</em> au moment de quitter l&rsquo;application et de l&rsquo;initialiser au début de <em>TFormRepert.FormCreate</em> :</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">procedure TFormRepert.FormClose(Sender: TObject; var Action: TCloseAction); <br />
var <br />
&nbsp; IniFile: TInifile; <br />
begin <br />
&nbsp; Inifile := TInifile.Create(ChangeFileExt(Application.ExeName,'.ini')); <br />
&nbsp; with IniFile do <br />
&nbsp; try <br />
&nbsp; &nbsp; WriteString('Data','FileName',CurrentfileName); <br />
&nbsp; finally <br />
&nbsp; &nbsp; Free; <br />
&nbsp; end; <br />
&nbsp; if ExtractFileExt(CurrentFileName) = '.fdb' then <br />
&nbsp; &nbsp; Exit; <br />
&nbsp; if MessageDlg('Sauvegarder ' + CurrentfileName + ' ?', <br />
&nbsp; mtConfirmation, [mbYes, mbNo], 0) = mrYes <br />
&nbsp; then <br />
&nbsp; &nbsp; aSaveExecute(Sender); <br />
end; <br />
&nbsp;<br />
procedure TFormRepert.FormCreate(Sender: TObject); <br />
var <br />
&nbsp; IniFile: TInifile; <br />
begin <br />
&nbsp; Inifile := TInifile.Create(ChangeFileExt(Application.ExeName,'.ini')); <br />
&nbsp; with IniFile do <br />
&nbsp; try <br />
&nbsp; &nbsp; CurrentFileName := ReadString('Data','FileName',''); <br />
&nbsp; finally <br />
&nbsp; &nbsp; Free; <br />
&nbsp; end; <br />
&nbsp; if not FileExists(CurrentFileName) then <br />
&nbsp; &nbsp; aOpenExecute(Sender) <br />
&nbsp; else begin <br />
&nbsp; &nbsp; ReadPeopleFromFile(CurrentFileName); <br />
&nbsp; &nbsp; Self.Caption := ExtractFileName(CurrentFileName); <br />
&nbsp; &nbsp; DisplayList; <br />
&nbsp; end; <br />
end;</div></div>
<p><strong>ATTENTION !</strong><br />
A ce stade, si l&rsquo;ajout, la mise à jour et la suppression semblent bien s&rsquo;effectuer correctement dans l&rsquo;interface-utilisateur, la base de données .fdb n&rsquo;est en fait pas mise à jour, les visiteurs <em>TSQLUpdatePerson</em>, <em>TSQLDeletePerson</em> et <em>TSQLInsertPerson</em> n&rsquo;ayant pas encore été implémentés.</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Persistance des objets : utilisation d&#8217;une base de données</title>
		<link>https://blog.developpez.com/erca57/p7150/prog-orientee-objets/persistance_des_objets_utilisation_d_une</link>
		<comments>https://blog.developpez.com/erca57/p7150/prog-orientee-objets/persistance_des_objets_utilisation_d_une#comments</comments>
		<pubDate>Sun, 01 Feb 2009 13:25:55 +0000</pubDate>
		<dc:creator><![CDATA[erca57]]></dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Delphi win32]]></category>
		<category><![CDATA[Persistance]]></category>
		<category><![CDATA[Prog. Orientée Objets]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Dans cet onzième épisode nous allons voir comment utiliser SQL pour stocker les objets dans une base de données. Vous aurez sans doute remarqué dans la livraison précédente que le code des actions de création/modification/suppression est fortement lié à la persistance à l&#8217;aide d&#8217;une StringList. Nous verrons plus tard qu&#8217;il nous faudra retravailler le code afin qu&#8217;il fonctionne indifféremment avec une StringList ou une base de données. Nous y reviendrons après avoir construit les visiteurs [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Dans cet onzième épisode nous allons voir comment utiliser SQL pour stocker les objets dans une base de données.</p>
<p>Vous aurez sans doute remarqué dans la livraison précédente que le code des actions de création/modification/suppression est fortement lié à la persistance à l&rsquo;aide d&rsquo;une <em>StringList</em>. Nous verrons plus tard qu&rsquo;il nous faudra retravailler le code afin qu&rsquo;il fonctionne indifféremment avec une <em>StringList</em> ou une base de données.</p>
<p>Nous y reviendrons après avoir construit les visiteurs pour mettre à jour, insérer et effacer un objet dans une base de données.</p>
<p><span id="more-11"></span><br />
<strong>2.2 &#8211; Persistance SQL (Firebird/Interbase)</strong></p>
<p>Pour la sauvegarde des objets dans une base de données, nous allons changer de stratégie. Ce n&rsquo;est pas la liste <em>People</em> des personnes qui sera écrite en bloc dans la base, mais chaque objet de la classe <em>TPerson</em> au fur et à mesure de sa création, de sa modification ou de sa suppression. Par contre nous continuerons à lire la liste de personnes en une fois au lancement de l&rsquo;application, à partir de la table <em>People</em>.<br />
Nous lisons toute la table au début, afin de pouvoir afficher la liste des personnes dans la <em>ListBox</em>. Cette liste peut ne pas afficher tous les champs de la table. Il faudra alors adapter la &laquo;&nbsp;décorateur&nbsp;&raquo; de l&rsquo;objet <em>TPeople</em> décrit précédemment (voir en 1.4).
</p>
<p>Commençons par le plus simple : la lecture des données.</p>
<p>Nous allons écrire un visiteur de l&rsquo;objet de la classe <em>TPeople</em> pour charger la liste à partir de la base de données. Pour les premiers tests nous aurons besoin d&rsquo;une base de données placée dans un fichier <em>PeopleDB.fdb</em> (base de données Firebird). Vous pouvez ausssi utiliser Interbase sans modifier grand chose au code.</p>
<p>Avant toute chose, il nous faut créer la table <em>People</em> dans la base <em>PeopleDB</em>, avec les champs suivants :</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &lt;strong&gt;OID&lt;/strong&gt; de type INTEGER, &nbsp;<br />
&nbsp; &lt;strong&gt;LastName&lt;/strong&gt; de type VARCHAR(20), &nbsp;<br />
&nbsp; &lt;strong&gt;FirstName&lt;/strong&gt; de type VARCHAR(20) et &nbsp;<br />
&nbsp; &lt;strong&gt;BirthDate&lt;/strong&gt; de type DATE.</div></div>
<p><strong>OID</strong> sera défini comme clé primaire. Une clé UNIQUE composée de <em>LastName</em>, <em>FirstName</em> et <em>BirhDate</em> peut s&rsquo;avérer utile pour éviter les doublons.</p>
<p>Notre premier objectif est d&rsquo;afficher la liste des personnes à partir du fichier <em>PeopleDB.fdb</em> que nous venons de créer. Nous créons à cette fin un visiteur de <em>TPeople</em> appelé <strong>TSQLReadPeople</strong>.</p>
<p><strong>2.2.1 &#8211; Visiteur TSQLReadPeople.</strong></p>
<p>Pour le visiteur de lecture de la table <em>People</em>, je me suis contenté de reproduire la solution élaborée par <em>TechInsite</em>. Je l&rsquo;aime pour sa simplicité et sa limpidité.</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; TSQLReadPeople = class(TSQLRead) <br />
&nbsp; protected <br />
&nbsp; &nbsp; procedure Init; override; <br />
&nbsp; &nbsp; procedure MapRowToObject; override; <br />
&nbsp; &nbsp; procedure SetupParams; override; <br />
&nbsp; public <br />
&nbsp; &nbsp; constructor Create(var aFileName: TFileName); overload; override; <br />
&nbsp; &nbsp; destructor Destroy; override; <br />
&nbsp; &nbsp; procedure VisitBizObj(Visited: TBizObj); override; <br />
&nbsp; end;</div></div>
<p>Nous avons 3 méthodes protégées et 3 méthodes publiques héritées toutes les six de <em>TSQLRead</em>, ancêtre de tous les visiteurs qui lisent une table SQL. La hiérarchie est la suivante:
</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; TSQLReadPeople -&gt; TSQLRead -&gt; TSQLFirebird -&gt; TBizObjVisitor</div></div>
<p>La connexion à la base s&rsquo;effectue dans le constructeur <em>Create</em> et la déconnexion dans le destructeur <em>Destroy</em>. Comme ces opérations sont communes à tous les visiteurs d&rsquo;accès à la base de données, que ce soit en lecture ou en écriture, la connexion et la déconnexion sont implémentées au niveau de l&rsquo;ancêtre <em>TSQLFirebird</em>.</p>
<p>Cette façon de procéder a un inconvénient majeur : à chaque création/modification/suppression de chaque objet, il y a connexion puis déconnexion, ce qui ralentit fortement l&rsquo;opération. Nous modifierons cette façon de faire ultérieurement.</p>
<p><strong>2.2.1.1 &#8211; Connexion à la base</strong></p>
<p>Comme les constructeurs des descendants de <em>TSQLFirebird</em> font appel au constructeur hérité (<code class="codecolorer text default"><span class="text">inherited Create</span></code>), c&rsquo;est le code ci-dessous qui est exécuté à la création de tout visiteur d&rsquo;accès à une base de données, ici Firebird.</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">constructor TSQLFirebird.Create(var aFileName: TFileName); <br />
begin <br />
&nbsp; inherited Create(aFileName); <br />
&nbsp; // aFileName must have .fdb extension <br />
&nbsp; if ExtractFileExt(aFileName) &lt;&gt; '.fdb' then <br />
&nbsp; &nbsp; ChangeFileExt(aFileName, '.fdb'); <br />
&nbsp; FDatabaseName := aFileName; <br />
&nbsp; //connexion à la base de données <br />
&nbsp; FDB := TIBDatabase.Create(nil); <br />
&nbsp; with FDB do <br />
&nbsp; begin <br />
&nbsp; &nbsp; DatabaseName := FDatabaseName; <br />
&nbsp; &nbsp; Params.Clear; <br />
&nbsp; &nbsp; Params.Add('user_name=USER'); <br />
&nbsp; &nbsp; Params.Add('password=password'); <br />
&nbsp; &nbsp; LoginPrompt := False; <br />
&nbsp; &nbsp; Connected := True; <br />
&nbsp; end; <br />
&nbsp;<br />
&nbsp; FTransaction := TIBTransaction.Create(nil); <br />
&nbsp; FTransaction.DefaultDatabase := FDB; <br />
&nbsp; FDB.DefaultTransaction := FTransaction; <br />
&nbsp;<br />
&nbsp; FQuery := TIBQuery.Create(nil); <br />
&nbsp; with FQuery do <br />
&nbsp; begin <br />
&nbsp; &nbsp; Database := FDB; <br />
&nbsp; &nbsp; Transaction := FTransaction; <br />
&nbsp; &nbsp; SQL.Clear; <br />
&nbsp; end; <br />
end;</div></div>
<p>Le code a été obtenu à partir d&rsquo;une petite application RAD d&rsquo;accès à la base de données. Le code généré par Delphi a été copié/collé dans le construteur <em>Create</em> ci-dessus. Y ont été ajoutées les 3 premières instructions:</p>
<ul>
<li>l&rsquo;appel du constructeur de l&rsquo;ancêtre,</li>
<li>la vérification de l&rsquo;extension du nom du fichier de base de données, et sa rectification éventuelle,</li>
<li>le stockage du nom de la base, avec son chemin d&rsquo;accès, dans le champ FDatabaseName de</li>
</ul>
<p>Le destructeur ci-dessous se contente de libérer les objets créés dans le constructeur.</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">destructor TSQLFirebird.Destroy; <br />
begin <br />
&nbsp; FQuery.Free; <br />
&nbsp; FTransaction.Free; <br />
&nbsp; FDB.Free; <br />
&nbsp; inherited Destroy; <br />
end;</div></div>
<p>Aucune autre méthode n&rsquo;est implémentée au niveau de <em>FSQLFirebird</em>, mis à part l&rsquo;appel inherited à la méthode de l&rsquo;ancêtre.</p>
<p>Revenons maintenant à notre visiteur <em>TSQLReadPeople</em>.</p>
<p><strong>2.2.1.2 &#8211; Lecture de la table</strong></p>
<p>Les 3 méthodes publiques ne sont pas implémentées à ce niveau et font donc appel à l&rsquo;ancêtre.<br />
Les 3 méthodes protégées sont implémentées dans <em>TSQLReadPeople</em>.
</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">procedure TSQLReadPeople.Init; <br />
begin <br />
&nbsp; FQuery.SQL.Clear; <br />
&nbsp; FQuery.SQL.Add('SELECT * FROM People ORDER BY OID;'); <br />
&nbsp; TPeople(FVisited).List.Clear; <br />
end;</div></div>
<p>La méthode <em>Init</em> se contente d&rsquo;écrire le code SQL dans <em>FQuery.SQL</em> ; comme il n&rsquo;y a pas de paramètre dans le code SQL, la méthode <em>SetupParams</em> reste vide. Des paramètres pourraient être utilisés pour remplacer <em>OID</em> dans l&rsquo;instruction <em>SELECT</em> afin de trier les données selon un autre critère, par exemple le Nom (<em>LastName</em>) et le Prénom (<em>FirstName</em>), voire la date de naissance.
</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">procedure TSQLReadPeople.SetupParams; <br />
begin <br />
&nbsp; // pas de paramètres à ce stade <br />
end;</div></div>
<p>La méthode <em>MapRowToObject</em> est chargée d&rsquo;affecter chaque champ d&rsquo;un enregistrement de la table récupéré par <em>FQuery</em> au champ correspondant de l&rsquo;objet de type <em>TPerson</em>, pour finalement ajouter l&rsquo;objet à la liste des personnes de type <em>TPeople</em> :</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">procedure TSQLReadPeople.MapRowToObject; <br />
var <br />
&nbsp; IData: TPerson; <br />
begin <br />
&nbsp; IData := TPerson.Create; <br />
&nbsp; IData.OID := FQuery.FieldByName('OID').AsInteger; <br />
&nbsp; IData.LastName := FQuery.FieldByName('LastName').AsString; <br />
&nbsp; IData.FirstName := FQuery.FieldByName('FirstName').AsString; <br />
&nbsp; IData.BirthDate := FQuery.FieldByName('BirthDate').AsDateTime; <br />
&nbsp; TPeople(FVisited).Add(IData); <br />
end;</div></div>
<p>Le code d&rsquo;itération des enregistrements de la table est implémenté au niveau de <em>TSQLRead</em>, parce qu&rsquo;il sert à tous les visiteurs destinés à lire les enregistrements d&rsquo;une table.</p>
<p><strong>2.2.1.3 &#8211; Parcours de la table</strong></p>
<p>C&rsquo;est donc le code ci-dessous qui est exécuté lorsqu&rsquo;on applique le visiteur <em>TSQLReadPeople</em> à l&rsquo;objet <em>TPeople</em>.</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">procedure TSQLRead.VisitBizObj(Visited: TBizObj); <br />
begin <br />
&nbsp; inherited VisitBizObj(Visited); <br />
&nbsp; Init; // Code SQL ; implémenté dans la classe concrète <br />
&nbsp; SetupParams; // Initialise les paramètres du code SQL. Implementé dans la classe concrète <br />
&nbsp; FQuery.Active := True; <br />
&nbsp; while not FQuery.EOF do begin <br />
&nbsp; &nbsp; MapRowToObject;//&quot;Mappe&quot; une requête avec un objet. Implementé dans la classe concrète <br />
&nbsp; &nbsp; FQuery.Next ; <br />
&nbsp; end ; <br />
&nbsp; Finalize; <br />
end;</div></div>
<p>En premier lieu il est fait appel à la méthode-ancêtre. En remontant la hiérarchie, on remarquera que cela a pour effet de stocker la valeur de <em>Visited</em> dans le champ <em>FVisited</em> qui sera ainsi disponible pour toutes les méthodes de tous les descendants.</p>
<p>Ensuite on exécute <em>Init</em> et <em>SetupParams</em> de <em>TSQLReadPerson</em>, puis on lance la requête en activant <em>FQuery</em>.</p>
<p>Finalement on parcourt les lignes renvoyées par <em>FQuery</em> et on en extrait les champs avec la méthode <em>MapRowToObject</em>, celle implémentée dans <em>TSQLReadPeople</em>.</p>
<p>N&rsquo;oublions pas d&rsquo;ajouter le code nécessaire dans la méthode <em>ReadPeopleFromFile</em> de <em>FormRepert</em> :</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">procedure TFormRepert.ReadPeopleFromFile(FN: TFileName); <br />
var <br />
&nbsp; CsvRead: TCsvTextFileRead; <br />
&nbsp; TxtRead: TFixedTextFileRead; <br />
&nbsp; FileExt: String; <br />
&nbsp; SqlRead: TSQLReadPeople; <br />
begin <br />
&nbsp; FileExt := ExtractFileExt(FN) ; <br />
&nbsp; if FileExt = '.csv' then begin <br />
&nbsp; &nbsp; CsvRead := TCsvTextFileRead.Create(FN); <br />
&nbsp; &nbsp; try <br />
&nbsp; &nbsp; &nbsp; People.AcceptBizObjVisitor(CsvRead); <br />
&nbsp; &nbsp; finally <br />
&nbsp; &nbsp; &nbsp; CsvRead.Free; <br />
&nbsp; &nbsp; end; <br />
&nbsp; end else if FileExt = '.txt' then begin <br />
&nbsp; &nbsp; TxtRead := TFixedTextFileRead.Create(FN); <br />
&nbsp; &nbsp; try <br />
&nbsp; &nbsp; &nbsp; People.AcceptBizObjVisitor(TxtRead); <br />
&nbsp; &nbsp; finally <br />
&nbsp; &nbsp; &nbsp; TxtRead.Free; <br />
&nbsp; &nbsp; end; <br />
&nbsp; end else if FileExt = '.fdb' then begin <br />
&nbsp; &nbsp; SqlRead := TSQLReadPeople.Create(FN); <br />
&nbsp; &nbsp; try <br />
&nbsp; &nbsp; &nbsp; People.AcceptBizObjVisitor(SqlRead); <br />
&nbsp; &nbsp; finally <br />
&nbsp; &nbsp; &nbsp; SqlRead.Free; <br />
&nbsp; &nbsp; end; <br />
&nbsp; end else <br />
&nbsp; &nbsp; ShowMessage('Format de fichier incorrect (extension '+FileExt+')'); <br />
end;</div></div>
<p>Efficace, non ?</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Persistance des objets : gestion d&#8217;une personne</title>
		<link>https://blog.developpez.com/erca57/p7041/prog-orientee-objets/persistance_des_objets_gestion_d_une_per</link>
		<comments>https://blog.developpez.com/erca57/p7041/prog-orientee-objets/persistance_des_objets_gestion_d_une_per#comments</comments>
		<pubDate>Tue, 06 Jan 2009 10:16:34 +0000</pubDate>
		<dc:creator><![CDATA[erca57]]></dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Delphi win32]]></category>
		<category><![CDATA[Persistance]]></category>
		<category><![CDATA[Prog. Orientée Objets]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Voilà le dixième article de la série consacrée à la persistance des objets. Il traite de la gestion d&#8217;une personne du répertoire : modification, création, suppression. 2.1.5.3 &#8211; Modifier une personne Pour pouvoir lancer la modification d&#8217;une personne, implémentons le code de l&#8217;action aEdit de la catégorie Person. procedure TFormRepert.aEditExecute(Sender: TObject); begin &#160; if ListBox1.ItemIndex &#60; 0 then begin &#160; &#160; MessageDlg('Sélectionnez une ligne', mtError, [mbOK], 0); &#160; &#160; Exit; &#160; end; &#160; TFormPerson.Execute(TPerson(People.List.Items[ListBox1.ItemIndex])); &#160; [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Voilà le <strong>dixième article</strong> de la série consacrée à la persistance des objets.</p>
<p>Il traite de la gestion d&rsquo;une personne du répertoire : modification, création, suppression.</p>
<p><span id="more-10"></span></p>
<p><strong>2.1.5.3 &#8211; Modifier une personne</strong></p>
<p>Pour pouvoir lancer la modification d&rsquo;une personne, implémentons le code de l&rsquo;action <em>aEdit</em> de la catégorie <em>Person</em>.</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">procedure TFormRepert.aEditExecute(Sender: TObject); <br />
begin <br />
&nbsp; if ListBox1.ItemIndex &lt; 0 then begin <br />
&nbsp; &nbsp; MessageDlg('Sélectionnez une ligne', mtError, [mbOK], 0); <br />
&nbsp; &nbsp; Exit; <br />
&nbsp; end; <br />
&nbsp; TFormPerson.Execute(TPerson(People.List.Items[ListBox1.ItemIndex])); <br />
&nbsp; DisplayList; <br />
end;</div></div>
<p>Tout d&rsquo;abord le code vérifie qu&rsquo;une ligne de la <em>ListBox</em> est bien sélectionnée. Si c&rsquo;est le cas, la méthode de classe <em>TFormPerson.Execute</em> est lancée, avec l&rsquo;objet à modifier en paramètre. L&rsquo;objet étant déjà inclus dans la liste <em>People</em>, aucune autre action n&rsquo;est nécessaire.<br />
Finalement <em>DisplayList</em> procède au rafraichissement de l&rsquo;affichage de la <em>ListBox</em>.
</p>
<p><strong>2.1.5.4 &#8211; Créer une personne</strong></p>
<p>La création d&rsquo;une personne est tout aussi simple.</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">procedure TFormRepert.aCreateExecute(Sender: TObject); <br />
var <br />
&nbsp; newPerson: TPerson; <br />
begin <br />
&nbsp; newPerson := TPerson.Create; <br />
&nbsp; TFormPerson.Execute(newPerson); <br />
&nbsp; if newPerson = nil then <br />
&nbsp; &nbsp; Exit; <br />
&nbsp; if newPerson.Dirty then <br />
&nbsp; &nbsp; begin <br />
&nbsp; &nbsp; People.Add(newPerson); <br />
&nbsp; &nbsp; DisplayList; <br />
&nbsp; &nbsp; end <br />
&nbsp; else <br />
&nbsp; &nbsp; newPerson.Free; <br />
end;</div></div>
<p>L&rsquo;insertion d&rsquo;une nouvelle personne nécessite la création d&rsquo;un nouvel objet <em>TPerson</em> vide : <em>newPerson</em>. Son état sera donc <em>bosEmpty</em>. Si nous validons la saisie dans <em>FormPerson</em> avec le bouton <em>Validate</em> qui met <em>Dirty</em> à True, l&rsquo;état passera de <em>bosEmpty</em> à <em>bosCreate</em>. Si <em>newPerson</em> est détruit, on abandonne l&rsquo;opération. Sinon, si l&rsquo;objet est <em>Dirty</em>, on l&rsquo;ajoute à la liste <em>People</em>. La méthode <em>People.Add</em> est donc sensée mettre l&rsquo;état de l&rsquo;objet à <em>bosClean</em>. Il faut modifier la méthode en conséquence:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">procedure TPeople.Add(aPerson: TPerson); <br />
begin <br />
&nbsp; aPerson.Dirty := False; <br />
&nbsp; FList.Add(aPerson); <br />
end;</div></div>
<p>Le fait de mettre <em>Dirty</em> à <em>false</em> a pour conséquence de mettre l&rsquo;état de l&rsquo;objet à <em>bosClean</em>.</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">procedure TBizObj.SetDirty(Value: Boolean); <br />
begin <br />
&nbsp; if Value = True then <br />
&nbsp; &nbsp; case FState of <br />
&nbsp; &nbsp; &nbsp; bosEmpty: &nbsp; FState := bosCreate; <br />
&nbsp; &nbsp; &nbsp; bosDeleted: FState := bosDelete; <br />
&nbsp; bosClean: &nbsp;FState := bosUpdate; <br />
&nbsp; &nbsp; &nbsp; //... <br />
&nbsp; &nbsp; end <br />
&nbsp; else <br />
&nbsp; &nbsp; FState := bosClean; <br />
end;</div></div>
<p><strong>2.1.5.5 &#8211; Supprimer une personne</strong></p>
<p>Le code pour l&rsquo;effacement d&rsquo;un objet est des plus dépouillés. Une seule ligne fait tout le travail.</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">procedure TFormRepert.aDeleteExecute(Sender: TObject); <br />
begin <br />
&nbsp; if ListBox1.ItemIndex &lt; 0 then begin <br />
&nbsp; &nbsp; MessageDlg('Sélectionnez une ligne', mtError, [mbOK], 0); <br />
&nbsp; &nbsp; Exit; <br />
&nbsp; end; <br />
&nbsp; if (MessageDlg('Effacer &quot;'+ListBox1.Items[ListBox1.ItemIndex]+'&quot; ?', <br />
&nbsp; mtConfirmation, [mbYes,mbNo], 0) = mrYes) <br />
&nbsp; then begin <br />
&nbsp; &nbsp; People.List.Delete(ListBox1.ItemIndex); <br />
&nbsp; &nbsp; DisplayList; <br />
&nbsp; end; <br />
end;</div></div>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Persistance de objets : refactoring de TPerson</title>
		<link>https://blog.developpez.com/erca57/p7035/prog-orientee-objets/persistance_de_objets_refactoring_de_tpe</link>
		<comments>https://blog.developpez.com/erca57/p7035/prog-orientee-objets/persistance_de_objets_refactoring_de_tpe#comments</comments>
		<pubDate>Mon, 05 Jan 2009 10:19:38 +0000</pubDate>
		<dc:creator><![CDATA[erca57]]></dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Delphi win32]]></category>
		<category><![CDATA[Persistance]]></category>
		<category><![CDATA[Prog. Orientée Objets]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Dans ce nouvel article de la série sur la persistance des objets, nous allons introduire la notion d&#8217;état ObjectState, nécessaire pour gérer la persistance dans une base de données SQL. 2.1.5.2 &#8211; Refactoring de TPerson Dans FormPerson est apparue la propriété Dirty de TPerson. La valeur de cette propriété est liée à l&#8217;état ObjectState de l&#8217;objet de type TPerson. Les états sont définis au niveau de l&#8217;ancêtre TBizObj de TPerson. Un descendant de TBizObj peut [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Dans ce nouvel article de la série sur la persistance des objets, nous allons introduire la notion d&rsquo;état <em>ObjectState</em>, nécessaire pour gérer la persistance dans une base de données SQL.<br />
<span id="more-9"></span></p>
<p><strong>2.1.5.2 &#8211; Refactoring de TPerson</strong></p>
<p>Dans <em>FormPerson</em> est apparue la propriété <em>Dirty</em> de <em>TPerson</em>. La valeur de cette propriété est liée à l&rsquo;état <em>ObjectState</em> de l&rsquo;objet de type <em>TPerson</em>. Les états sont définis au niveau de l&rsquo;ancêtre <em>TBizObj</em> de <em>TPerson</em>. Un descendant de <em>TBizObj</em> peut en effet être vide de toute donnée quand il vient juste d&rsquo;être créé. Il sera alors dans l&rsquo;état <em>bosEmpty</em> (<em>bos</em> pour BizzObj State).<br />
Si nous saisissons et validons des données pour un objet à l&rsquo;état <em>bosEmpty</em>, il passera à l&rsquo;état <em>bosCreate</em> qui signifie que cet objet doit être ajouté à la liste ou à la table <em>People</em>. Dès qu&rsquo;il aura été ajouté, il passera à l&rsquo;état <em>bosClean</em>, ce qui signifie qu&rsquo;il est persistant.<br />
Si l&rsquo;objet est modifié, il passe à l&rsquo;état <em>bosUpdate</em>. Il devra donc être sauvegardé pour repasser à <em>bosClean</em>.<br />
S&rsquo;il doit être supprimé, son état sera mis à <em>bosDelete</em>. Dès qu&rsquo;il aura été supprimé physiquement dans la liste d&rsquo;objets ou dans la table, il passera pas à l&rsquo;état <em>bosDeleted</em>. Cela a pour conséquence qu&rsquo;il ne sera plus affiché. Mais il est toujours possible de le passer à l&rsquo;état <em>bosCreate</em> si on veut le recréer</p>
<p>Mais revenons à la propriété <em>Dirty</em>. Elle est définie comme suit :<br />
<code class="codecolorer text default"><span class="text">Dirty := ObjectState in [bosCreate, bosUpdate, bosDelete]</span></code>.<br />
Un objet <em>Dirty</em> nécessite donc d&rsquo;être enregistré ou supprimé. Quand, comme dans <em>TFormPerson.bbtnValidateClick</em>, la propriété <em>Dirty</em> est mise à <em>True</em>, l&rsquo;état de l&rsquo;objet est mis à une des valeurs <em>bosCreate</em>, <em>bosUpdate</em> et <em>bosDelete</em>, en fonction de son état courant.</p>
<p>La propriété <em>Deleted</em> permet de savoir rapidement si l&rsquo;objet doit être affiché ou non :<br />
<code class="codecolorer text default"><span class="text">Deleted := ObjecState in [bosDelete, bosDeleted]</span></code>.</p>
<p>Enfin la propriété <em>OID</em> servira d&rsquo;identifiant pour l&rsquo;enregistrement éventuel de l&rsquo;objet dans une table SQL.</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; TBizObjID = Int64; <br />
&nbsp;<br />
&nbsp; TObjectState = //le préfixe bos signifie BizObj State <br />
&nbsp; &nbsp; (bosEmpty, &nbsp; //le BizObject vient d'être créé et est encore vide <br />
&nbsp; &nbsp; bosCreate, &nbsp; //le bo est à insérer dans le fichier ou la base de données <br />
&nbsp; &nbsp; bosUpdate, &nbsp; //le bo est à mettre à jour dans le fichier ou la bdd <br />
&nbsp; &nbsp; bosDelete, &nbsp; //le bo est à supprimer du fichier ou de la bdd <br />
&nbsp; &nbsp; bosDeleted, &nbsp;//le bo a été supprimé dans le fichier ou la bdd <br />
&nbsp; &nbsp; bosClean); &nbsp; //l'état du bo correspond à son état dans le fichier ou la bdd <br />
&nbsp;<br />
&nbsp; TBizObj = class(TObject) <br />
&nbsp; private <br />
&nbsp; &nbsp; FOID: TBizObjID; <br />
&nbsp; &nbsp; FState: TObjectState; <br />
&nbsp; &nbsp; function GetDeleted: Boolean; <br />
&nbsp; &nbsp; function GetDirty: Boolean; <br />
&nbsp; &nbsp; function GetOID: TBizObjID; <br />
&nbsp; &nbsp; procedure SetDirty(Value: Boolean); <br />
&nbsp; public <br />
&nbsp; &nbsp; procedure AcceptBizObjVisitor(Visitor: TBizObjVisitor); virtual; <br />
&nbsp; &nbsp; property Deleted: Boolean read GetDeleted; <br />
&nbsp; &nbsp; property Dirty: Boolean read GetDirty write SetDirty; <br />
&nbsp; &nbsp; property OID: TBizObjID read GetOID write FOID; <br />
&nbsp; &nbsp; property State: TObjectState read FState write FState; <br />
&nbsp; end;</div></div>
<p>Vous trouverez ci-dessous l&rsquo;implementation des nouvelles propriétés:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">function TBizObj.GetDeleted: Boolean; <br />
begin <br />
&nbsp; Result := (FState in [bosDelete, bosDeleted]); <br />
end; <br />
&nbsp;<br />
function TBizObj.GetDirty: Boolean; <br />
begin <br />
&nbsp; Result := (FState in [bosCreate, bosUpdate, bosDelete]); <br />
end; <br />
&nbsp;<br />
function TBizObj.GetOID: TBizObjID; <br />
var <br />
&nbsp; DT: TDateTime; <br />
begin <br />
&nbsp; if FOID = 0 then begin <br />
&nbsp; &nbsp; //générer un nouvel identifiant basé sur l'instant courant <br />
&nbsp; &nbsp; DT := Now - StrToDate('31/08/08'); <br />
&nbsp; &nbsp; Result := ( ( trunc(DT) * 1000000 ) + ( trunc(frac(DT) * 1000000) ) ); <br />
&nbsp; end; <br />
&nbsp; Result := FOID; <br />
end; <br />
&nbsp;<br />
procedure TBizObj.SetDirty(Value: Boolean); <br />
begin <br />
&nbsp; if Value = True then <br />
&nbsp; &nbsp; case FState of <br />
&nbsp; &nbsp; &nbsp; bosEmpty: &nbsp; FState := bosCreate; <br />
&nbsp; &nbsp; &nbsp; bosDeleted: FState := bosDelete; <br />
&nbsp; bosClean: &nbsp;FState := bosUpdate; <br />
&nbsp; &nbsp; &nbsp; //... <br />
&nbsp; &nbsp; end <br />
&nbsp; else <br />
&nbsp; &nbsp; FState := bosClean; <br />
end;</div></div>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Persistance des objets : gestion des personnes</title>
		<link>https://blog.developpez.com/erca57/p6987/prog-orientee-objets/persistance_des_objets_gestion_des_perso</link>
		<comments>https://blog.developpez.com/erca57/p6987/prog-orientee-objets/persistance_des_objets_gestion_des_perso#comments</comments>
		<pubDate>Sun, 21 Dec 2008 17:20:56 +0000</pubDate>
		<dc:creator><![CDATA[erca57]]></dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Delphi win32]]></category>
		<category><![CDATA[Persistance]]></category>
		<category><![CDATA[Prog. Orientée Objets]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Voici enfin la suite de ma série d&#8217;articles sur un exemple de persistance d&#8217;objets. 2.1.5 &#8211; Gestion des personnes Nous allons maintenant implémenter les trois actions non activées de la catégorie Person : aEdit, aCreate et aDelete. Pour éditer les propriétés ou attributs d&#8217;un objet Person, nous allons créer un formulaire de saisie FormPerson dans l&#8217;unité uFPerson. 2.1.5.1 &#8211; Formulaire de saisie Le formulaire FormPerson est présenté dans la figure ci-dessous en mode de conception [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Voici enfin la suite de ma série d&rsquo;articles sur un exemple de persistance d&rsquo;objets.</p>
<p><strong>2.1.5 &#8211; Gestion des personnes</strong></p>
<p>Nous allons maintenant implémenter les trois actions non activées de la catégorie <em>Person</em> : <em>aEdit</em>, <em>aCreate</em> et <em>aDelete</em>.<br />
Pour éditer les propriétés ou attributs d&rsquo;un objet <em>Person</em>, nous allons créer un formulaire de saisie <em>FormPerson</em> dans l&rsquo;unité <em>uFPerson</em>.</p>
<p><strong>2.1.5.1 &#8211; Formulaire de saisie</strong></p>
<p>Le formulaire <em>FormPerson</em> est présenté dans la figure ci-dessous en mode de conception :</p>
<p><img src="http://blog.developpez.com/media/Figure 4 - FormPerson en mode conception.jpg" width="257" height="164" alt="FormPerson en mode conception" /><br />
<span id="more-8"></span></p>
<p>Il contient:<br />
&#8211; trois <em>TLabel</em> : <em>LabelLastName</em> affichant &laquo;&nbsp;Nom :&nbsp;&raquo;, <em>LabelFirstName</em> affichant &laquo;&nbsp;Prénom :&nbsp;&raquo; et <em>LabelBirthDate</em> affichant &laquo;&nbsp;Date de naissance :&nbsp;&raquo;<br />
&#8211; deux <em>TEdit</em> : <em>EditLastName</em> et <em>EditFirstName</em>;<br />
&#8211; un <em>TDateTimePicker</em> : <em>DateTimePickerBirth</em>;<br />
&#8211; un <em>TBitButton</em> de type OKButton : <em>bbtnValidate</em> avec le texte &laquo;&nbsp;Valider&nbsp;&raquo;.</p>
<p>Les développeurs de <em>tiOPF</em> utilisent une méthode de classe <em>Execute</em> pour afficher le formulaire de saisie à la façon des composants  Dialogues de Delphi. Je n&rsquo;ai pu m&rsquo;empêcher de les imiter. Tout le code concernant la mise à jour des données est ainsi encapsulé dans <em>TFormPerson</em>.<br />
Vous remarquerez la propriété <em>Data</em> qui permet de synchroniser l&rsquo;objet de classe <em>TPerson</em> passé en paramètre dans <em>Create</em> et les champs <em>EditFirstName</em>, <em>EditLastName</em> et <em>DateTimePickerBirth</em>.
</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; TFormPerson = class(TForm) <br />
&nbsp; &nbsp; bbtnValidate: TBitBtn; <br />
&nbsp; &nbsp; DateTimePickerBirth: TDateTimePicker; <br />
&nbsp; &nbsp; EditFirstName: TEdit; <br />
&nbsp; &nbsp; EditLastName: TEdit; <br />
&nbsp; &nbsp; LabelBirthDay: TLabel; <br />
&nbsp; &nbsp; LabelFirstName: TLabel; <br />
&nbsp; &nbsp; LabelLastName: TLabel; <br />
&nbsp; &nbsp; procedure bbtnValidateClick(Sender: TObject); <br />
&nbsp; private <br />
&nbsp; &nbsp; FData: TPerson; <br />
&nbsp; &nbsp; function GetData: TPerson; <br />
&nbsp; &nbsp; procedure SetData(Value: TPerson); <br />
&nbsp; public <br />
&nbsp; &nbsp; class function Execute(aPerson: TPerson): Boolean; <br />
&nbsp; &nbsp; property Data: TPerson read GetData write SetData; <br />
&nbsp; end;</div></div>
<p>L&rsquo;implémentation est des plus dépouillées:
</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">procedure TFormPerson.bbtnValidateClick(Sender: TObject); <br />
begin <br />
&nbsp; Data.Dirty:= True; <br />
&nbsp; ModalResult := mrOK; <br />
end;</div></div>
<p>La nouveauté dans le code ci-dessus est la propriété <em>Dirty</em> de la classe <em>TPerson</em>. Mettre <em>Dirty</em> à <em>True</em> sert à informer que l&rsquo;objet courant n&rsquo;a pas été ajouté à la liste <em>People</em> ou enregistré dans la table <em>People</em> d&rsquo;une base de données.</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">class function TFormPerson.Execute(aPerson: TPerson): Boolean; <br />
begin <br />
&nbsp; FormPerson := TFormPerson.Create(nil); <br />
&nbsp; with FormPerson do <br />
&nbsp; try <br />
&nbsp; &nbsp; Data := aPerson; <br />
&nbsp; &nbsp; ShowModal; <br />
&nbsp; finally <br />
&nbsp; &nbsp; Free; <br />
&nbsp; end; <br />
end;</div></div>
<p>La méthode de classe ne présente pas de difficulté particulière. Notez que l&rsquo;objet à éditer dans le formulaire est passé en paramètre. La puissance de cette façon de faire réside dans les méthodes <em>GetData</em> et <em>SetData</em>.</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">function TFormPerson.GetData: TPerson; <br />
begin <br />
&nbsp; FData.FirstName := EditFirstName.Text; <br />
&nbsp; FData.LastName := EditLastName.Text; <br />
&nbsp; FData.BirthDate := DateTimePickerBirth.Date; <br />
&nbsp; Result := FData; <br />
end; <br />
&nbsp;<br />
procedure TFormPerson.SetData(Value: TPerson); <br />
begin <br />
&nbsp; FData := Value; <br />
&nbsp; EditFirstName.Text := FData.FirstName; <br />
&nbsp; EditLastName.Text := FData.LastName; <br />
&nbsp; DateTimePickerBirth.Date := FData.BirthDate; <br />
end;</div></div>
<p><em>GetData</em> et <em>SetData</em> permettent de synchroniser les champs du formulaire avec les champs de l&rsquo;objet <em>Data</em> qui n&rsquo;est autre que l&rsquo;objet  <em>aPerson</em> passé en paramètre de la méthode de classe <em>Execute</em>.</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Persistance des objets : GUI de l&#8217;exemple</title>
		<link>https://blog.developpez.com/erca57/p6832/prog-orientee-objets/persistance_des_objets_gui_de_l_exemple</link>
		<comments>https://blog.developpez.com/erca57/p6832/prog-orientee-objets/persistance_des_objets_gui_de_l_exemple#comments</comments>
		<pubDate>Tue, 25 Nov 2008 12:09:31 +0000</pubDate>
		<dc:creator><![CDATA[erca57]]></dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Delphi win32]]></category>
		<category><![CDATA[Persistance]]></category>
		<category><![CDATA[Prog. Orientée Objets]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[2.1.4 &#8211; Nouvelle interface-utilisateur Après avoir montré comment sauvegarder et lire des objets dans des fichiers-textes, je vous propose de compléter l&#8217;interface-utilisateur. La figure 2 ci-dessus montre FormRepert en mode conception. Remarquez les boutons et les composants qui ont été ajoutés, ainsi que les images affectées aux boutons et qui sont contenues dans ImageList1. ImageList1 est affectée aux propriétés Images de ToolBar1, ActionList1 et MainMenu1. Les actions contenues dans ActionList1 sont aNew, aOpen, aSave, aSaveAs [&#8230;]]]></description>
				<content:encoded><![CDATA[<p><strong>2.1.4 &#8211; Nouvelle interface-utilisateur</strong></p>
<p>Après avoir montré comment sauvegarder et lire des objets dans des fichiers-textes, je vous propose de compléter l&rsquo;interface-utilisateur.</p>
<p><img src="http://blog.developpez.com/media/Figure 2 - FormRepert en mode conception.jpg" width="221" height="301" alt="Intreface-utilisateur" /><br />
La figure 2 ci-dessus montre <em>FormRepert</em> en mode conception.
</p>
<p><span id="more-7"></span>
<p>
Remarquez les boutons et les composants qui ont été ajoutés, ainsi que les images affectées aux boutons et qui sont contenues dans ImageList1.
</p>
<p>
<em>ImageList1</em> est affectée aux propriétés <em>Images</em> de <em>ToolBar1</em>, <em>ActionList1</em> et <em>MainMenu1</em>.
</p>
<p>
Les actions contenues dans <em>ActionList1</em> sont <strong>aNew</strong>, <strong>aOpen</strong>, <strong>aSave</strong>, <strong>aSaveAs</strong> et <strong>aExit</strong> dans la catégorie <strong>File</strong>, et <strong>aCreate</strong>, <strong>aModify</strong> et <strong>aDelete</strong> dans la catégorie <strong>Person</strong>.
</p>
<p>
A ce stade ne sont implémentées que les méthodes correspondant aux actions du groupe <em>File</em>:
</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">procedure TFormRepert.aExitExecute(Sender: TObject); <br />
begin <br />
&nbsp; Close; <br />
end; <br />
&nbsp;<br />
procedure TFormRepert.aNewExecute(Sender: TObject); <br />
begin <br />
&nbsp; if (MessageDlg('Voulez-vous enregistrer le fichier courant ?', <br />
&nbsp; mtWarning, [mbYes, mbNo], 0) = mrYes) <br />
&nbsp; then <br />
&nbsp; &nbsp; aSaveExecute(Sender); <br />
&nbsp; People.Clear; <br />
&nbsp; ListBox1.Items := PeopleList.Strings; <br />
end; <br />
&nbsp;<br />
procedure TFormRepert.aOpenExecute(Sender: TObject); <br />
begin <br />
&nbsp; if OpenDialog1.Execute then begin <br />
&nbsp; &nbsp; CurrentFileName := OpenDialog1.FileName; <br />
&nbsp; &nbsp; ReadPeopleFromFile(CurrentFileName); <br />
&nbsp; &nbsp; ListBox1.Items := PeopleList.Strings; <br />
&nbsp; end; <br />
end; <br />
&nbsp;<br />
procedure TFormRepert.aSaveAsExecute(Sender: TObject); <br />
begin <br />
&nbsp; //enregistrer dans un fichier différent <br />
&nbsp; SaveDialog1.InitialDir := ExtractFilePath(CurrentFileName); <br />
&nbsp; SaveDialog1.DefaultExt := ExtractFileExt(CurrentFileName); <br />
&nbsp; if SaveDialog1.Execute then begin <br />
&nbsp; &nbsp; CurrentFileName := SaveDialog1.FileName; <br />
&nbsp; &nbsp; SavePeopleToFile(CurrentfileName); <br />
&nbsp; end; <br />
end; <br />
&nbsp;<br />
procedure TFormRepert.aSaveExecute(Sender: TObject); <br />
begin <br />
&nbsp; //enregistrement dans le fichier actif, sinon enregistrer sous <br />
&nbsp; if not FileExists(CurrentFileName) then <br />
&nbsp; &nbsp; aSaveAsExecute(Sender) <br />
&nbsp; else begin <br />
&nbsp; &nbsp; SavePeopleToFile(CurrentFileName); <br />
&nbsp; &nbsp; MessageDlg('Le fichier'+#13+#10+CurrentFileName+#13+#10 <br />
&nbsp; &nbsp; +'a été correctement enregistré', mtInformation, [mbOK], 0); <br />
&nbsp; end; <br />
end;</div></div>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
