TDataSetProxy เป็นส่วนประกอบ wrapper สำหรับส่วนประกอบชุดข้อมูล Delphi แบบคลาสสิก อนุญาตให้แทนที่ชุดข้อมูลใด ๆ ด้วยชุดข้อมูลปลอม (ตารางในหน่วยความจำ) สามารถใช้พร็อกซีเพื่อแยกชั้นธุรกิจออกจากชุดข้อมูลได้ การแยกนี้จะมีประโยชน์เมื่อจำเป็นต้องใส่รหัสธุรกิจลงในชุดทดสอบอัตโนมัติ (การทดสอบหน่วย)
แรงบันดาลใจ . Idea ขึ้นอยู่กับรูปแบบ Proxy GoF และรูปแบบ Active Record ซึ่งกำหนดโดย Martin Fowler ใน รูปแบบหนังสือของสถาปัตยกรรมแอปพลิเคชันระดับองค์กร
รูปแบบ DataSet Proxy มีประโยชน์ในระหว่างการแยกตรรกะทางธุรกิจ สิ่งนี้อาจเป็นประโยชน์อย่างยิ่งสำหรับการปรับปรุงโครงการเดิมที่มีการเชื่อมโยงกันสูง เมื่อรหัสการผลิตขึ้นอยู่กับข้อมูล SQL และการเชื่อมต่อ SQL เป็นเรื่องยากมากที่จะเขียนการทดสอบหน่วยสำหรับรหัสดังกล่าว
การแทนที่ชุดข้อมูลด้วยพรอกซีจะทำให้เกิดระดับนามธรรมใหม่ซึ่งสามารถช่วยได้ทั้ง: ชุดข้อมูล SQL ในรหัสการผลิตและชุดข้อมูลหน่วยความจำในโครงการทดสอบ พร็อกซีมีอินเทอร์เฟซ (รายการวิธีการ) ที่คล้ายกันมากกับชุดข้อมูลแบบคลาสสิก ซึ่งช่วยในการโยกย้ายได้ง่าย ชุดข้อมูลปลอมจะอนุญาตให้ตรวจสอบ (ยืนยัน) รหัสการผลิตโดยไม่ต้องเชื่อมต่อกับฐานข้อมูล
DataSet Proxy ร่วมกับโปรเจ็กต์คู่กัน 2 โปรเจ็กต์ (DataSet Generator, Delphi Command Pattern) เปิดโอกาสให้นักพัฒนาแนะนำการทดสอบหน่วยด้วยการปรับโครงสร้างใหม่อย่างปลอดภัย
พร็อกซีชุดข้อมูลเป็นโซลูชันชั่วคราว และหลังจากครอบคลุมโค้ดกับวิศวกรทดสอบแล้ว ก็สามารถใช้การปรับโครงสร้างขั้นสูงเพิ่มเติมได้ เช่น การแยกโค้ดหรือทำให้สามารถประกอบและนำกลับมาใช้ใหม่ได้มากขึ้น เนื่องจากหนึ่งในพร็อกซีการปรับโครงสร้างใหม่เหล่านี้สามารถแทนที่ได้อย่างปลอดภัยด้วยวัตถุ DAO หรือโดยโครงสร้างข้อมูลแบบจำลอง
นักพัฒนาจะได้เรียนรู้วิธีเขียนโค้ดที่สะอาดขึ้นหรือวิธีใช้วิธีทดสอบก่อนและทำงานได้ดีขึ้นร่วมกับโค้ดและการปรับปรุงคุณภาพ
โครงการสนับสนุน:
โครงการ | ที่เก็บ GitHub |
---|---|
ตัวสร้างชุดข้อมูล | https://github.com/bogdanpolak/dataset-generator |
โปรเจ็กต์ประกอบด้วยซอร์สโค้ดของคลาสพื้นฐาน TDataSetProxy
และตัวสร้างพร็อกซีสองประเภทที่แตกต่างกัน:
src/Comp.Generator.DataProxy.pas
TDataSetProxy
tools/generator-app
ส่วนประกอบ TDataProxyGenerator
มีประโยชน์เมื่อวิศวกรต้องการสร้างพร็อกซีสำหรับการออกจากชุดข้อมูลในรหัสการผลิต นี่เป็นงานง่าย ๆ สองขั้นตอน: (1) เพิ่มหน่วยส่วนประกอบเพื่อใช้ส่วน (2) ค้นหาโค้ดโดยใช้ชุดข้อมูลและวิธีการสร้างการโทร:
รหัสการผลิตปัจจุบัน:
aBooksDataSet := fDBConnection.ConstructSQLDataSet(
aOwner, APPSQL_SelectBooks);
dbgridBooks.DataSource.Dataset := aBooksDataSet;
รหัสเครื่องกำเนิดไฟฟ้าที่ฉีด:
TDataProxyGenerator.SaveToFile( ' ../../src/Proxy.Books ' ,
aBooksDataSet, ' TBookProxy ' );
แอป Generator สำหรับ FireDAC เป็นเครื่องมือทางเลือกที่สร้างขึ้นเพื่อการสาธิตเป็นส่วนใหญ่ ในทางปฏิบัติ การใช้เครื่องมือนี้อาจมีประโยชน์น้อยกว่าการใช้ตัวสร้างส่วนประกอบโดยตรง แอพ Generator มีไว้สำหรับการฝึกสอนและการฝึกอบรมโดยเฉพาะ สำหรับข้อมูลเพิ่มเติม โปรดตรวจสอบ: แอปตัวสร้างสำหรับ FireDAC - คู่มือผู้ใช้
type
TBookProxy = class (TDatasetProxy)
private
fISBN :TWideStringField;
fTitle :TWideStringField;
fReleseDate :TDateField;
fPages :TIntegerField;
fPrice :TBCDField;
protected
procedure ConnectFields ; override;
public
property ISBN :TWideStringField read fISBN;
property Title :TWideStringField read fTitle;
property ReleseDate :TDateField read fReleseDate;
property Pages :TIntegerField read fPages;
property Price :TBCDField read fPrice;
end ;
procedure TBookProxy.ConnectFields ;
begin
Assert(fDataSet.Fields.Count = 5 );
fISBN := fDataSet.FieldByName( ' ISBN ' );
fTitle := fDataSet.FieldByName( ' Title ' );
fReleseDate := fDataSet.FieldByName( ' ReleseDate ' );
fPages := fDataSet.FieldByName( ' Pages ' );
fPrice := fDataSet.FieldByName( ' Price ' );
end ;
ส่วนประกอบ DataSetProxy เป็นคลาสพร็อกซีซึ่งมีวิธีการที่เกือบจะเหมือนกันกับส่วนประกอบ TDataSet แบบคลาสสิก นักพัฒนาสามารถแทนที่ส่วนประกอบ DataSet ใดๆ ด้วยพร็อกซีนี้ได้อย่างง่ายดาย โดยใช้การเปลี่ยนแปลงโค้ดการผลิตเพียงเล็กน้อยและมีความเสี่ยงต่ำ จากมุมมองรหัสการผลิตการเปลี่ยนแปลงมีขนาดเล็กและไม่สำคัญมากนัก แต่จากมุมมองของการทดสอบ นี่คือการเปลี่ยนแปลงพื้นฐาน เนื่องจากนักพัฒนาสามารถกำหนดค่าพร็อกซีใหม่เพื่อใช้ชุดข้อมูลหน่วยความจำน้ำหนักเบา
วิธี TDataSetProxy
ส่วนใหญ่เป็นเพียงการโคลนของ TDataSet เพียงครั้งเดียว คุณสามารถขยายชุดของวิธีการนี้ได้โดยเพิ่มการหายไปเพียงครั้งเดียวหรือสร้างวิธีการใหม่ที่ไม่ซ้ำใคร วิธีการพร็อกซีนี้คือ: Append
, Edit
, Cancel
, Delete
, Close
, Post
, RecordCount
, First
, Last
, Eof
, Next
, Prior
, EnableControls
, DisableControls
, Locate
, Lookup
, Refresh
และอื่นๆ เอกสารและวิธีการใช้งานนี้เหมือนกับเอกสาร Delphi มาตรฐานสำหรับคลาส TDataSet
วิธี TDataSetProxy
ที่เหลือสามารถแบ่งออกเป็นสองกลุ่ม: วิธีการตั้งค่าพร็อกซี (การกำหนดค่า) และวิธีการช่วยเหลือพร็อกซี (ขยายฟังก์ชันการทำงานของชุดข้อมูลแบบคลาสสิก)
procedure TDataModule1.OnCreate (Sender: TObject);
begin
fOrdersProxy := TOrdersProxy.Create(fOwner);
fOrdersDataSource := fOrdersProxy.ConstructDataSource;
end ;
procedure TDataModule1.InitOrders (aYear, aMonth: word);
begin
fOrdersProxy.WithFiredacSQL( FDConnection1,
' SELECT OrderID, CustomerID, OrderDate, Freight ' +
' FROM {id Orders} WHERE OrderDate between ' +
' :StartDate and :EndDate ' ,
[ GetMonthStart(aYear, aMonth),
GetMonthEnd(aYear, aMonth) ],
[ftDate, ftDate])
.Open;
fOrdersInitialized := True;
end ;
procedure TDataModule1.InitOrders (aDataSet: TDataSet);
begin
fOrdersProxy.WithDataSet(aDataSet).Open;
fOrdersInitialized := True;
end ;
คอมโพเนนต์ TDataSetProxy
รุ่นปัจจุบันมีวิธีการช่วยเหลือเพียงวิธีเดียวซึ่งนำมาใช้เป็นตัวอย่าง นักพัฒนาสามารถขยายคอลเลกชันนี้ตามหลักปฏิบัติในการเขียนโค้ดของทีม ข้อเสนอแนะในการขยายคลาสพร็อกซีคือการใช้การสืบทอด ตัวอย่างการใช้วิธี ForEach
helper ที่มีอยู่:
function TDataModule.CalculateTotalOrders ( const aCustomerID: string): Currency;
begin
Result := 0 ;
fOrdersProxy.ForEach(procedure
begin
if fOrdersProxy.CustomerID. Value = aCustomerID then
Result := Result + fOrdersProxy.GetTotalOrderValue;
end ;
end ;
Comp.Generator.DataProxy.pas
SaveToFile
SaveToClipboard
Execute
Code
DataSet
GeneratorMode
DataSetAccess
FieldNamingStyle
NameOfUnit
NameOfClass
IndentationText
ตัวเลือก | ค่านิยม | คำอธิบาย |
---|---|---|
GeneratorMode | ( pgmClass , pgmUnit ) | สร้างเฉพาะส่วนหัวของคลาสและการใช้งานหรือหน่วยทั้งหมดด้วยคลาส |
NameOfUnit | String | ชื่อของหน่วยที่สร้างขึ้นใช้ในการสร้างส่วนหัวของหน่วย |
NameOfClass | String | ชื่อของคลาสพร็อกซีที่สร้างขึ้น |
FieldNamingStyle | ( fnsUpperCaseF , fnsLowerCaseF ) | ตัดสินใจว่าฟิลด์คลาสจะถูกตั้งชื่ออย่างไร: ใช้ตัวพิมพ์ใหญ่ต่อท้าย F หรือตัวพิมพ์เล็ก |
IndentationText | String | ข้อความที่ใช้สำหรับการเยื้องโค้ดแต่ละรายการ ค่าเริ่มต้นคือช่องว่างสองช่อง |
DataSetAccess | ( dsaNoAccess , dsaGenComment , dsaFullAccess ) | กำหนดการเข้าถึงชุดข้อมูลพร็อกซีภายใน: การเข้าถึงแบบเต็ม = คุณสมบัติอ่านอย่างเดียวถูกสร้างขึ้นเพื่อให้มีการเข้าถึง ไม่มีตัวเลือกการเข้าถึงเป็นค่าเริ่มต้นและแนะนำ |
ในการสร้างคลาส poxy คุณสามารถใช้วิธี Execute แต่ก่อนที่จะเรียกมัน คุณควรตั้งค่าตัวเลือกและคุณสมบัติ DataSet
ทั้งหมด หลังจากเรียกใช้โค้ดที่สร้าง Execute
จะถูกจัดเก็บไว้ใน TStringList
ภายในที่สามารถเข้าถึงได้ผ่านคุณสมบัติ Code
ดูโค้ดตัวอย่างด้านล่าง:
aProxyGenerator:= TDataProxyGenerator.Create(Self);
try
aProxyGenerator.DataSet := fdqEmployees;
aProxyGenerator.NameOfUnit := ' Proxy.Employee ' ;
aProxyGenerator.NameOfClass := ' TEmployeeProxy ' ;
aProxyGenerator.IndentationText := ' ' ;
aProxyGenerator.Execute;
Memo1.Lines := aProxyGenerator.Code;
finally
aProxyGenerator.Free;
end ;
วิธีที่ง่ายกว่าและกะทัดรัดในการสร้างคลาสพร็อกซีคือการใช้วิธีการสร้างคลาสตัวสร้าง: SaveToFile
หรือ SaveToClipboard
ชื่อของมันมีความหมายเพียงพอที่จะเข้าใจฟังก์ชันการทำงานของพวกเขา SaveToFile สร้างทั้งหน่วยและเขียนลงในไฟล์ และ SaveToClipboard สร้างเฉพาะคลาสและเขียนลงใน Windows Clipboard ดูตัวอย่างด้านล่าง:
TDataProxyGenerator.SaveToFile(
' src/Proxy.Employee ' ,
fdqEmployees,
' TEmployeeProxy ' ,
' '
fnsLowerCaseF);
TDataProxyGenerator.SaveToClipboard(
fdqEmployees,
' TEmployeeProxy ' ,
' '
fnsLowerCaseF);
โครงการนี้เป็นผลมาจากประสบการณ์หลายปีและหลายทีม ทีมงานนี้พบว่าแนวทาง Delphi ที่อิงตามเหตุการณ์แบบคลาสสิกไม่เพียงแต่มีประสิทธิผลน้อยลง แต่ยังเป็นอันตรายต่อนักพัฒนา ผู้จัดการ และลูกค้าอีกด้วย
การทำงานกับ RDBMS (เซิร์ฟเวอร์ SQL) ใน Delphi ดูเหมือนจะมีประสิทธิผลและเรียบง่ายมาก นักพัฒนาทิ้งองค์ประกอบ Query
ป้อนคำสั่ง SQL ตั้งค่าคุณสมบัติ Active เชื่อมต่อตัวควบคุม DB-aware ทั้งหมดเข้ากับ Query และคุณก็ทำเสร็จแล้ว ... เกือบเสร็จแล้ว เกือบจะห่างไกลจากความพร้อมที่จะส่งมอบแอปพลิเคชันจริงๆ
การใช้นักพัฒนารูปแบบภาพที่เรียบง่ายนี้สามารถเปิดเผยและแก้ไขข้อมูลเซิร์ฟเวอร์ SQL ได้อย่างรวดเร็ว ในความเป็นจริงแล้ว สิ่งที่ดูเหมือนง่ายสำหรับการขอทาน แต่อย่างหลังกลับกลายเป็นความท้าทาย ภายในเวลาที่กำหนด วิศวกรจะสร้างชุดข้อมูลและเหตุการณ์มากขึ้นเรื่อยๆ จัดเรียงข้อมูลทางธุรกิจและผสมผสานการนำเสนอ การกำหนดค่า และรหัสโดเมน โปรเจ็กต์เริ่มยุ่งวุ่นวายและเชื่อมโยงกันมากขึ้นเรื่อยๆ หลังจากผ่านไปหลายปี ผู้จัดการและนักพัฒนาก็สูญเสียการควบคุมโครงการดังกล่าว: ไม่สามารถระบุแผนและกำหนดเวลาได้ ลูกค้ากำลังดิ้นรนกับข้อบกพร่องที่ไม่คาดคิดและแปลกประหลาด การเปลี่ยนแปลงง่ายๆ ต้องใช้เวลาทำงานหลายชั่วโมง
การแทนที่ชุดข้อมูลแบบคลาสสิกด้วยพร็อกซีต้องใช้เวลาในการเรียนรู้และตรวจสอบการทำงานจริง วิธีการนี้อาจดูแปลกเล็กน้อยสำหรับนักพัฒนา Delphi แต่ง่ายต่อการนำไปใช้และเรียนรู้ ด้วยแรงจูงใจด้านการจัดการและทีมฝึกสอนวิศวกรอาวุโสจะนำการดึงโค้ดมาใช้และแทนที่ชุดข้อมูลด้วยเทคนิคพรอกซีได้เร็วขึ้น
วิธีการใช้พร็อกซีที่กำหนดไว้ในที่นี้คือเทคนิคการปรับโครงสร้างใหม่ที่เรียบง่ายและปลอดภัยโดยเฉพาะสำหรับแอปพลิเคชัน VCL แบบคลาสสิกที่สร้างด้วยวิธี EDP (Event Driven Programming) การใช้โซลูชันนี้ในรูปแบบวิวัฒนาการที่มีขนาดเล็ก แต่ส่วนสำคัญของรหัสธุรกิจสามารถแยกออกมาและครอบคลุมด้วยการทดสอบหน่วย หลังจากผ่านไประยะหนึ่ง ด้วยเครือข่ายความปลอดภัยที่ดีกว่า (ครอบคลุมการทดสอบหน่วย) วิศวกรสามารถสลับพร็อกซีกับ OOP DAO และปรับปรุงโค้ดได้มากขึ้นโดยใช้การปรับโครงสร้างใหม่และรูปแบบสถาปัตยกรรมขั้นสูง
กระบวนการปรับปรุงให้ทันสมัยประกอบด้วยขั้นตอนต่อไปนี้:
ดูตัวอย่างที่แสดงเส้นทางการย้ายของโปรเจ็กต์ VCL เดิมโดยใช้ TDataSetProxy เราจะเริ่มต้นด้วยวิธีการแบบคลาสสิกที่กำหนดไว้ในรูปแบบ:
procedure TFormMain.LoadBooksToListBox ();
var
aIndex: integer;
aBookmark: TBookmark;
aBook: TBook;
isDatePrecise: boolean;
begin
ListBox1.ItemIndex := - 1 ;
for aIndex := 0 to ListBox1.Items.Count - 1 do
ListBox1.Items.Objects[aIndex].Free;
ListBox1.Clear;
aBookmark := fdqBook.GetBookmark;
try
fdqBook.DisableControls;
try
while not fdqBook.Eof do
begin
aBook := TBook.Create;
ListBox1.AddItem(fdqBook.FieldByName( ' ISBN ' ).AsString + ' - ' +
fdqBook.FieldByName( ' Title ' ).AsString, aBook);
aBook.ISBN := fdqBook.FieldByName( ' ISBN ' ).AsString;
aBook.Authors.AddRange(BuildAuhtorsList(
fdqBook.FieldByName( ' Authors ' ).AsString));
aBook.Title := fdqBook.FieldByName( ' Title ' ).AsString;
aBook.ReleaseDate := ConvertReleaseDate(
fdqBook.FieldByName( ' ReleaseDate ' ).AsString);
aBook.Price := fdqBook.FieldByName( ' Price ' ).AsCurrency;
aBook.PriceCurrency := fdqBook.FieldByName( ' Currency ' ).AsString;
ValidateCurrency(aBook.PriceCurrency);
fdqBook.Next;
end ;
finally
fdqBook.EnableControls;
end
finally
fdqBook.FreeBookmark(aBookmark);
end ;
end ;
สังเกต! วิธีแก้ปัญหาที่นำเสนอข้างต้นเป็นแนวทางปฏิบัติที่ไม่ดี แต่น่าเสียดายที่นักพัฒนา Delphi มักใช้ เป้าหมายของการใช้ TDataProxy คือการปรับปรุงสถานะนี้และแยกตรรกะทางธุรกิจออกจากการแสดงภาพ
วิธีนี้จะโหลดข้อมูลจากฐานข้อมูล SQL โดยใช้ fdqBook
TFDQuery ออบเจ็กต์ของคลาส TBook
ถูกสร้างขึ้นสำหรับแต่ละแถว โดยฟิลด์ของคลาสจะเต็มไปด้วยค่าชุดข้อมูลและตรวจสอบความถูกต้อง เนื่องจากวัตถุ TBook
ถูกเก็บไว้ในตัวควบคุม TListBox
ซึ่งเป็นเจ้าของวัตถุเหล่านั้นด้วย วิธีการนี้ต้องนำออกใช้ก่อน
เราแทนที่ชุดข้อมูลด้วยวัตถุพร็อกซี นอกจากนี้ เรากำลังปรับปรุงโค้ดให้ทันสมัยโดยการเปลี่ยนลูป while-not-eof
แบบคลาสสิกด้วยวิธี ForEach
ที่ใช้งานได้ ในเวลาเดียวกัน เรากำลังเปิดตัวรูปแบบที่ปลอดภัยยิ่งขึ้นในการเข้าถึงค่าของฟิลด์ คุณสามารถแยกระยะนี้เป็น 3 ระยะ แต่สำหรับบทความนี้ เราจำเป็นต้องทำให้เนื้อหามีขนาดกะทัดรัด
procedure TFormMain.LoadBooksToListBox ();
var
aIndex: integer;
aBook: TBook;
begin
ListBox1.ItemIndex := - 1 ;
for aIndex := 0 to ListBox1.Items.Count - 1 do
ListBox1.Items.Objects[aIndex].Free;
ListBox1.Clear;
fProxyBooks.ForEach(
procedure
begin
aBook := TBook.Create;
ListBox1.AddItem(fProxyBooks.ISBN. Value + ' - ' +
fProxyBooks.Title. Value , aBook);
aBook.ISBN := fProxyBooks.ISBN. Value ;
aBook.Authors.AddRange(
BuildAuhtorsList(fProxyBooks.Authors. Value ));
aBook.Title := fProxyBooks.Title. Value ;
aBook.ReleaseDate := ConvertReleaseDate(
fProxyBooks.ReleaseDate. Value );
aBook.Price := fProxyBooks.Price.AsCurrency;
aBook.PriceCurrency := fProxyBooks.Currency. Value ;
ValidateCurrency(aBook.PriceCurrency);
end );
end ;
รหัสสามารถอ่านได้ง่ายกว่าและปลอดภัยกว่า แต่ยังคงอยู่ในรูปแบบ ถึงเวลาที่จะต้องลบออกและแยกออกจากการอ้างอิงทั้งหมดเพื่อเปิดใช้งานการทดสอบ
เราต้องเริ่มต้นด้วยการตัดสินใจทางสถาปัตยกรรมที่สำคัญ ขณะนี้ในโค้ดเรามีสองคลาสที่คล้ายกัน: TBook
จัดเก็บข้อมูลและ TBookProxy
ประมวลผลข้อมูลเหล่านั้น สิ่งสำคัญคือต้องตัดสินใจว่าคลาสใดเหล่านี้ขึ้นอยู่กับคลาสอื่น TBook
เป็นส่วนหนึ่งของเลเยอร์โมเดลและไม่ควรคำนึงถึงออบเจ็กต์การเข้าถึงข้อมูล
procedure TForm1.LoadBooksToListBox ();
begin
ListBox1.Clear;
fProxyBooks.LoadAndValidate;
fProxyBooks.FillStringsWithBooks(ListBox1.Items);
end ;
ในที่สุด วิธีการสร้างแบบฟอร์มก็ดูดีและชัดเจน นี่เป็นสัญญาณที่ดีว่าเรากำลังไปในทิศทางที่ถูกต้อง โค้ดที่แยกและย้ายไปยังพร็อกซีชุดข้อมูลดูเหมือนเกือบจะเหมือนก่อนหน้านี้:
procedure TBooksProxy.LoadAndValidate ;
var
aBook: TBook;
isDatePrecise: boolean;
begin
fBooksList.Clear;
ForEach(
procedure
begin
aBook := TBook.Create;
fBooksList.Add(aBook);
aBook.ISBN := ISBN. Value ;
aBook.Authors.AddRange(
BuildAuhtorsList(Authors. Value ));
aBook.Title := Title. Value ;
aBook.ReleaseDate := ConvertReleaseDate(
ReleaseDate. Value , isDatePrecise);
aBook.IsPreciseReleaseDate := isDatePrecise;
aBook.Price := Price.AsCurrency;
aBook.PriceCurrency := Currency. Value ;
ValidateCurrency(aBook.PriceCurrency);
end );
end ;
เมื่อใช้ร่วมกับโค้ดนี้ เราจะต้องย้ายวิธีการที่ต้องพึ่งพาทั้งหมดที่รับผิดชอบในการแปลงและตรวจสอบข้อมูล: BuildAuhtorsList
, ConvertReleaseDate
และ ValidateCurrency
พร็อกซีนี้มีคอลเลกชันภายในของหนังสือ fBookList
ซึ่งใช้ในการกรอกกล่องรายการ ในขณะนั้นเราย้ายโค้ดนี้ไปยังคลาสพร็อกซีชุดข้อมูลเพื่อลดจำนวนการเปลี่ยนแปลง แต่ควรย้ายรหัสนี้ไปยังคลาสที่เหมาะสม:
procedure TBooksProxy.FillStringsWithBooks (
aStrings: TStrings);
var
aBook: TBook;
begin
aStrings.Clear;
for aBook in fBooksList do
aStrings.AddObject(
aBook.ISBN + ' - ' + aBook.Title, aBook);
end ;
TBookProxy
ใน (หน่วย Data.Proxy.Book.pas
)function CreateMockTableBook
ใน (หน่วย Data.Mock.Book.pas
)