Design Pattern : Momento

    ผมไม่ได้เขียนเรื่องเกี่ยวกับ OO (Object Oriented) มานาน วันนี้สบโอกาส ก็เลยจะเขียนซักหน่อย เป็นเรื่องเกี่ยวกับ Design Pattern ซึ่งเป็นเรื่องยอดนิยมมาก  แต่ถ้าคุณไม่รู้จักว่า Design Pattern คืออะไร ผมมีรายละเอียด ที่จะนำเสนอในบทความ Tour de France #3 : Object Oriented ไว้รออ่านก็แล้วกัน

ที่มา

    ผมกำลังวางแผนสร้าง Framework ตัวใหม่ บน .NET  สำหรับงานในบริษัทครับ ผมตั้งใจจะให้ทีมพัฒนาโปรแกรมทุกคน เขียนโปรแกรมเป็นแบบ OO เต็มรูปแบบ (ซะที) จากการที่ใช้ Visual Basic มานาน มันก็ติดๆ อยู่พอสมควร คราวนี้สมใจแล้วครับ จะได้ใช้ VB.NET ที่รองรับ OO เต็มรูปแบบ

    แต่ถึงจะเป็น VB.NET  ที่รองรับ OO ได้เต็มรูปแล้ว แต่โปรแกรมที่จะเขียนขึ้น มันจะเป็น OO เต็มรูปไม่ได้ ถ้าผมยังใช้วิธีเดิมๆ ในการติดต่อกับฐานข้อมูล ซึ่งมันก็คือการใช้คำสั่ง SQL เชื่อมไปกับ Microsoft SQL Server ไม่ใช่ว่า SQL Server ไม่ดีนะครับ เพียงแต่ว่า การเขียนโปรแกรมลักษณะนั้น มันไม่เป็น OO นั่นเอง การที่จะให้เป็น OO เต็มรูปแบบนั้น ต้องใช้ ฐานข้อมูลแบบ ODBMS ครับ ไม่ใช่ RDBMS

    ผมใช้เวลาศึกษา ODBMS แต่ละเจ้ามาเป็นเวลานาน โชคร้ายครับที่บน .NET ยังไม่มี ODBMS ตัวใดรองรับเลย จะมีพอไปวัดไปวาได้ ก็คือ Cache แต่ Cache ก็รองรับเพียงแค่ COM/ActiveX และเมื่อผมลองใช้งานดู ก็ปรากฏว่ามันใช้งานยาก ยุ่งยากพอสมควร

    มี ODBMS ตัวหนึ่งที่ผมลองแล้วชอบมาก นั่นคือ Objectivity ผมลองใช้ภาษา Java เขียนโปรแกรมทดสอบดู ปรากฏว่าติดใจครับ การใช้งานง่ายมาก เป็นธรรมชาติ ถึงแม้ว่ามันจะทำลายความเป็น OO เล็กน้อยก็ตาม แต่ภาพโดยรวมดูดีมากครับ น่าเสียดายมากครับที่ Objectivity ยังไม่รองรับ .NET

    ผมเองก็ไม่มีเวลาพอที่จะรอ Objectivity ให้รองรับ .NET ผมต้องเริ่มทำ Framework แล้ว ผมลองมาคิดย้อนดูว่า ผมมีทางเลือกอะไรบ้าง

ทางเลือกที่ 1

    ใช้ Java มันเลยให้รู้แล้วรู้รอด ฟรีๆ ทั้งนั้น ยกเว้นตัว Objectivity ที่ต้องซื้อ ซึ่งเรื่องนี้ ไม่เป็นที่ยอมรับครับ โปรแกรมเมอร์ในทีม ไม่มีใครเอาด้วย ผมรู้คำตอบก่อนถามแล้วด้วยซ้ำไป

ทางเลือกที่ 2

    ใช้ VB ต่อกับ Cache ซึ่งดูไม่ดีอีกนั่นแหละครับ เพราะ VB มันไม่รองรับ OO เต็มรูปแบบ

ทางเลือกที่ 3

    ใช้ VB.NET ต่อเข้ากับ SQL Server เหมือนเดิม แต่ผมต้องทำตัว Wrapper หรือ Mapper เพื่อบังเอาความเป็น RDBMS ออกจาก สายตาของโปรแกรมเมอร์ ซึ่งแนวคิดนี้ ทุกคนเห็นด้วย เพราะถ้า ถึงยังไง ถ้าการใช้ ODBMS ไม่ประสบผลสำเร็จ ตัวข้อมูลจริงๆ มันยังอยู่ใน SQL Server ซึ่ง เราก็ยังสามารถเขียนคำสั่ง SQL เพื่อดึงออกมาใช้งานได้อีก

 

แนวคิดจาก Objectivity

    ผมเลือกทางเลือกที่ 3 แต่ผมคงไม่เขียนวิธีการที่ทำให้ Object กลายเป็น Persistent ได้อย่างไร มันเกินขอบเขตของบทความนี้ เอาเป็นว่า ผมมี Class อยู่ตัวหนึ่งที่ชื่อว่า PersistentObject ซึ่ง class นี้จะบัง โปรแกรมเมอร์จาก RDBMS

    จากนั้น ผมก็จะสร้าง Business Logic พวก class ต่างๆ ที่ใช้ในเนื้องาน โดยที่ class เหล่านั้น ต้อง Inherit มาจาก PersistentObject มันจะได้มีคุณสมบัติของ Persistent  ซึ่งแนวคิดนี้ผมเอาอย่างมาจาก Objectivity นั่นเอง แล้วด้วย PersistentObject นี้เอง จะทำให้ โปรแกรมเมอร์สามารถเขียนโปรแกรมเป็นแบบ OO ได้เต็มรูปแบบ โดยไม่ต้องยุ่งกับ RDBMS

 

ปัญหา Transaction

    อะไรมันก็ดูไปได้ด้วยดี แต่คิดไปคิดมา มันติดปัญหาอยู่อย่างหนึ่ง ปัญหาใหญ่พอสมควร นั่นก็คือ เมื่อเราติดต่อกับฐานข้อมูล ไม่ว่าจะเป็น RDBMS หรือ ODBMS ก็ตาม คุณสมบัติที่จำเป็นอย่างหนึ่งที่ DBMS เหล่านี้ต้องมีก็คือเรื่องของ Transaction  นั่นเอง Transaction ก็คือการกระทำงานใดๆ ที่ต้องให้ผลว่าสำเร็จ หรือ ไม่ได้ทำเลย จะไม่มีตรงกลางว่าทำไปแล้วบางส่วน ดังนั้นเมื่อการทำงานนั้นๆ กระทำไปถึงกลางทาง แล้วพบปัญหาที่ไม่สามารถทำต่อจนสำเร็จ ตัว DBMS นั้นจะมีความสามารถที่จะ RollBack กลับไปยังจุดเริ่มต้น เสมือนว่ายังไม่เคยทำเลย ซึ่งเชื่อว่าคุณคงรู้จักเรื่องนี้เป็นอย่างดีแล้ว

    ความสามารถในการทำ Transaction มีใน DBMS เกือบทุกตัว ตัวไหนไม่มีก็ไม่ควรจะไปใช้ครับ ODBMS ก็เช่นกัน คุณสมบัตินี้เป็นสิ่งที่จำเป็น

    แต่ลองนึกภาพดูครับ เมื่อ Business ต่างๆ ถูกปรับปรุงข้อมูลใน Transaction ถ้ามีคำสั่งฟ้าผ่าออกมาให้ RollBack สิ่งที่ทำทั้งหมด แน่นอนครับ DBMS ที่ผมใช้คือ SQL Server มีความสามารถนี้เป็นพื้นฐานอยู่แล้ว มันสามารถ RollBack ข้อมูลได้ แต่สิ่งที่ไม่มีก็คือ Business Object ของผมเองที่เซ่อไม่รู้เรื่องเลย ไม่สามารถ RollBack ข้อมูลได้

ความพยายามที่ให้ Business Object รองรับ Transaction

    อย่างแรกที่ผมนึกถึงเลยนะครับ ก็คือ Business Object ทุกตัวต้องสร้าง Methods 2 ตัว นั่นคือ SaveState() และ RestoreState() ซึ่งเมื่อเรียก Methods SaveState() แล้ว ตัวแปรทุกตัวใน Object นั้นจะถูกแอบ copy เก็บไว้อีกชุดหนึ่ง และเมื่อเรียก RestoreState() ในกรณีที่เราต้องการ RollBack ตัว RestoreState() จะต้องเอาทุกค่าที่แอบเก็บไว้ ลอกคืนมาทั้งหมด ซึ่งวิธีนี้ก็ดูไม่เลว แต่คุณจะต้องเหนื่อยมาก สิ่งที่คุณต้องทำมีดังนี้ครับ

    ซึ่งมันกินแรงพอสมควรครับ ถ้าเราสร้างตัวแปรตัวใหม่ขึ้นมา คุณต้องแก้โปรแกรมเพิ่มอีก 3 จุดข้างบน เพื่อให้มันทำงานได้ ซึ่งดูไม่ดี

ออกแบบ Pattern

    ผมจำได้ว่าเมื่อสมัยที่ผมหัดเรียน Design Pattern ของ Gang of Four ผมใช้วิธีดูปกในที่สรุปย่อๆ 2-3 บรรทัดว่า Design Pattern ทั้ง 23 ตัว แต่ละตัวทำอะไร จากนั้น ผมก็ลองออกแบบดู ว่าถ้าต้องการให้ทำเช่นนั้นได้ ต้องออกแบบอย่างไร จากนั้นผมค่อยเอาไปเปรียบเทียบกับของ Gang of Four มองหาจุดอ่อนที่เราออกแบบ ดูว่าของ Gang of Four ที่เป็นระดับปรมาจารย์ เขาออกแบบกันอย่างไร ผมว่าวิธีการเรียนแบบนี้ก็ไม่เลว มันจะได้ใช้สมองคิด ไม่ใช่มัวลอกอย่างเดียว

    มี Pattern ตัวหนึ่งใน 23 ตัวของ Design Pattern ชื่อว่า Memento คำศัพท์แปลกดี เมื่อเปิด dictionary ดูก็พบว่า มันคือของที่ระลึก มีเอาไว้เพื่อเตือนใจให้ระลึกถึง (ผ้าเช็ดหน้า??) มันเป็น Pattern สำหรับเอาไว้แก้ปัญหาของผมโดยเฉพาะเลยครับ

    แต่ก่อนที่ผมจะศึกษา Pattern Memento ก็อย่างที่บอกเอาไว้ว่า ผมก็คิดของผมเองไว้ก่อนแล้ว ผลออกมาว่า สิ่งที่ผมคิดนั้นออกไปคนละทางกับ Pattern Memento เลย ในสมัยนั้น พอผมศึกษา Pattern Memento ผมรู้สึกอึดอัด ว่าทำไมปรมาจารย์ถึงออกแบบได้ประหลาดขนาดนั้น

Pattern Memento ของ Gang of Four

    แนวคิดของ Memento คือการสร้าง Object ขึ้นมาตัวหนึ่ง ฝังเอาไว้อยู่ใน Business Class ของเรา ซึ่ง object ตัวนี้ทำหน้าที่เป็นกล่องดวงใจ เก็บตัวแปรทั้งหมดของ Business Class จากนั้นเมื่อต้องการให้เก็บ State ก็ทำการลอกเอาข้อมูลทั้งหมดของ Business Class ไปเก็บไว้ในกล่องดวงใจนี้ จากนั้นก็ส่งกล่องดวงใจออกไปข้างนอก ไม่ต้องห่วงนะครับ เมื่อภายนอกได้กล่องดวงใจไป ก็ไม่สามารถที่จะแอบอ่านข้อมูลได้ ซึ่งภายนอกจะจัดตั้งผู้ดูแลที่ชื่อว่า CareTaker เอาไว้ดูแลกล่องดวงใจ จนกระทั่งเมื่อเกิดการ RollBack เกิดขึ้น CareTaker จะส่งกล่องดวงใจนี้กลับไปให้กับ Business Object เพื่อปรับข้อมูลให้ตรงกับค่าในกล่องดวงใจ มิน่าหละ ถึงตั้งชื่อว่า Memento

    สิ่งที่ผมรู้สึกขัดใจกับวิธีนี้ก็คือ ทำไมต้องส่งกล่องดวงใจออกข้างนอก จริงอยู่ที่ กล่องดวงใจนี้ ยังคง Encapsulation โดยที่ไม่ให้ผู้อื่นแอบดูข้อมูล แต่การส่งออกข้างนอกนี้ มันทำให้เกิดสภาวะที่เรียกว่า God Class ตัว Business Class ไม่สามารถดูแลข้อมูลของตัวเองได้ทั้งหมด ต้องให้ผู้อื่นจัดการให้ มันผิดหลักการ OO ที่ผมเคยเรียนรู้มาก่อน (จริงๆ แล้วอาจจะไม่ได้ผิดอะไร ผมอาจจะความรู้น้อยเกินไปจนไม่เข้าใจก็เป็นได้ แต่กับความรู้ที่ผมมีอยู่ตอนนี้ผมว่ามันผิดหลักการแน่ๆ ถ้าใครมีความเห็นยังไงมาคุยกันที่ webboard ได้นะครับ)

    ยิ่งไปกว่านั้น ในงานของผม Business Class มันยังมี Inheritance ได้อีกหลายชั้นเป็น Hierarchy แต่ Pattern Memento นั้นดูเหมือนว่าไม่ได้ออกแบบมารองรับให้มีการ Inherit นี่ก็เป็นความเข้าใจของผม ใครที่มีแนวคิดที่แตกต่าง ก็มาคุยกันนะครับ 

    Pattern Momento นั้นไม่ได้ช่วยเราลดงานจากวิธีแรกเลย ทุกอย่างมีเท่ากัน แถมยัง มีเพิ่มเติมให้ดูยุ่งขึ้นอีก ภายนอกก็ต้องยุ่งมากขึ้น ต้องหาวิธีดูแลกล่องดวงใจ ของแต่ละ Object

    ในตัวอย่างที่ GOF แนะนำนั้นใช้ภาษา C++ ทางออกในการที่คง Encapsulation ได้นั้น ก็คือ การที่ ตัว object ของกล่องดวงใจ ต้องทำให้ตัวแปรทุกตัวเป็น private มีแต่ Business Class เท่านั้นที่เป็น friend จึงสามารถเข้าไปจัดการข้อมูลในกล่องดวงใจได้ ซึ่ง object อื่นๆ ไม่มีสิทธิครับ

    ภาษา C# ไม่มี Friend แต่มี Modifier ตัวที่ 4 นอกเหนือจาก public, private และ protected นั่นก็คือ internal การใช้ internal หมายถึง จะ public สำหรับ code ที่อยู่ใน Assembly เดียวกัน แต่จะเป็น private สำหรับข้างนอก ดังนั้นเราต้องกำหนด Modifier ของตัวแปรทุกตัวในกล่องดวงใจให้เป็น internal ซึ่ง Business Class และ กล่องดวงใจ อยู่ใน Assembly เดียวกัน จึงสามารถถ่ายโอนข้อมูลกันได้ แต่ Code ที่เรียกใช้อยู่คนละ Assembly จึงไม่สามารถเรียกใช้งานได้ แต่การทำเช่นนี้ก็มีจุดอ่อนเช่นกัน มัน Encapsulate ไม่สมบูรณ์ class อื่นที่อยู่ใน Assembly เดียวกัน ก็สามารถหลุดเข้าไปอ่านเขียนข้อมูลในกล่องดวงใจได้

    ส่วน Java นั้น มีทางออกที่ดีกว่า คือการสร้าง กล่องดวงใจ ให้เป็น Inner Class ของ Business Class ซึ่งมันอาจจะดูรกซักหน่อย แต่ Java ก็มีกติกาว่า Outer Class สามารถจัดการข้อมูลของ Inner Class ได้ ถึงแม้ว่า Inner Class จะกำหนดตัวแปรเป็น Private ก็ตาม แต่ถ้าทำแบบนี้ private ก็ไม่ใช่ private จริงๆ

    ในความเห็นของผม friend ของ C++, internal ของ C# หรือ การยอมให้ Outer Class เข้าถึงข้อมูล private ของ Inner Class ล้วนแล้วแต่ไม่ใช่ความคิดที่ดีในเชิง OO โดยเฉพาะอย่างยิ่งมีผู้เห็นด้วยมากว่า friend ของ C++ นี้ตัวอันตรายมาก ไม่น่าใช้งาน

    ความจำเป็นในการเอากล่องดวงใจออกมา ผมเชื่อว่ามี เท่าที่พอนึกออก ก็คือความสามารถในการที่จะ undo ได้หลายๆ steps แถมยังภายนอกยังสามารถควบคุมการ RollBack ได้ตามใจอีกด้วย

Pattern Momento

    อย่างที่ผมบอกไว้ในตอนแรกว่า ผมก็ลองหัดออกแบบดูเหมือนกัน ผมคงไม่บังอาจกล่าวว่า มันดีกว่าของ GOF แต่สำหรับปัญหาข้างบน ถ้าว่าไปแล้ว ความสามารถและความยืดหยุ่นคงน้อยกว่าของ GOF แต่ผมว่าสำหรับงานของผมที่ไม่ต้องการความซับซ้อนอะไร ผมว่ามันเหมาะกว่า ผู้ใช้สามารถใช้งานเป็น Black Box ได้เลย ไม่มีการเอากล่องดวงใจออกไปเก็บข้างนอก ผมตั้งชื่อว่า Momento เลียนแบบเสียเลย

    ที่ตั้งชื่อว่า Momento ก็คือการเก็บ state Moment นั้น เก็บเอาไว้ หลังจากนั้น ก็มีความสามารถในการ RollBack ถ้าต้องการ เรามาลองดู Code กันเลยดีกว่า

class Start
{
	public static void Main()
	{
		Secretary s = new Secretary();
		s.SetTimeOut(60);
		s.SetDepartment("Marketing");
		s.SetLanguages(3);

 		s.SaveState();		
		s.SetTimeOut(120);
		s.SetDepartment("Management");
		s.SetLanguages(4);

		s.RestoreState();	
		Console.WriteLine(s.GetTimeOut());
		Console.WriteLine(s.GetDepartment());
		Console.WriteLine(s.GetLanguages());
	}
}

    ผมแสดงให้เห็นถึงวิธีใช้กันก่อน จะสังเกตว่า แนวทางที่ใช้เป็นแนวทางแบบแรกคือ Object มี 2 Methods คือ SaveState() และ RestoreState() ไม่มีการเอากล่องดวงใจส่งออกข้างนอก ผลลัพธ์ที่ได้จาก Code นี้เป็นดังนี้ครับ

DOS Prompt

C:\CS>momento
60
Marketing
3

C:\CS>_

    ที่ Class Secretary นี้ มันไม่ใช่ Class โดดๆ นะครับ มัน Inherit มาจาก class Employee ซึ่ง Class Employee นั้นก็ Inherit มาจาก PersistentObject เพื่อความสามารถในการทำ Persistent นั่นเอง

    ค่า TimeOut ได้มาจาก class PersistentObject, ค่า Department ได้มาจาก class Employee และค่า Languages ได้มาจาก class Secretary ครับ

คราวนี้เรามาลองดู class Secretary

 

class Secretary : Employee
{
	private int _intLanguages = 1;
	public void SetLanguages(int @intLanguages) {
		_intLanguages = @intLanguages;
	}	
	public int GetLanguages()
	{
		return _intLanguages;
	}	
	public Secretary() { }	
	protected override void CopyMomento(PersistentObject @sobj) // Momento Pattern
	{
		Secretary sec = (Secretary)@sobj;	
		_intLanguages = sec._intLanguages;
		base.CopyMomento(sec);
	}
	protected Secretary(Secretary @sec)    // Momento Pattern 
	{  
		CopyMomento(@sec);
	}
}

    จะเห็นได้ว่า สิ่งที่ Momento Pattern ให้ทำนั้น มีเพียง 2 Methods ที่เป็น Protected เท่านั้น นั่นก็คือ CopyMomento ที่ Inherit ยาวลงมาจาก Persistent Object ซึ่ง Method นี้ จะรับ Object ชนิดเดียวกันกับตัวเอง ในที่นี้คือ Secretary ดังนั้นให้คุณถ่ายข้อมูลทั้งหมดจาก object ที่เป็น parameter ลงมาที่ object ปัจจุบันครับ แล้วให้เรียก ฟังก์ชันเดียวกันนี้กับ Base class ของมัน และ Method ที่ 2 คือ Constructor ครับ ถ้าเป็นภาษา C++ คงเรียก Constructor ตัวนี้ว่า Copy Constructor มันคือ Constructor มีรับพารามิเตอร์เป็น object ชนิดเดียวกันกับมัน ซึ่งปกติแล้ว เราจะทำการคัดลอก ค่าจาก Object นั้นใส่มาที่ตัวเรา ซึ่งผมก็ได้เรียกใช้บริการของ CopyMomento() ครับ

    class Employee ก็ใช้แนวคิดเดียวกัน ดังนี้ครับ

class Employee : PersistentObject
{
	private string _strDepartment;

	public void SetDepartment(string @strDept)
	{
		_strDepartment = @strDept;
	}	
	public string GetDepartment()
	{
		return _strDepartment;
	}
	public Employee() { }
	protected override void CopyMomento(PersistentObject @pobj) // Momento Pattern
	{
		Employee emp = (Employee)@pobj;	
		_strDepartment = emp._strDepartment;
		base.CopyMomento(emp);
	}	
	protected Employee(Employee @emp) // Momento Pattern
	{
		CopyMomento(@emp);
	}
}

  คราวนี้ถึงหัวใจแล้วครับ คือ PersistentObject ซึ่งความซับซ้อนมันถูกซ่อนอยู่ในนี้ทั้งหมด ลองดู Code กันก่อนครับ

abstract class PersistentObject
{
	private int _intSQLTimeOut = 120;	
	public PersistentObject() { }
	public void SetTimeOut(int @intTimeOut)
	{
		_intSQLTimeOut = @intTimeOut;
	}
	public int GetTimeOut()
	{
		return _intSQLTimeOut;
	}	
 	private  PersistentObject _momento = null;  // Momento Pattern 	
	private PersistentObject(PersistentObject @pobj) // Momento Pattern
	{
		CopyMomento(@pobj);
	}	
	public void SaveState()  // Momento Pattern
	{
		object[] o = {this};
		_momento = (PersistentObject)Activator.CreateInstance(this.GetType(), o);
	}
	public void RestoreState()  // Momento Pattern
	{
		CopyMomento(_momento);
		_momento = null;
	}	
	protected virtual void CopyMomento(PersistentObject @pobj) // Momento Pattern
	{
		_intSQLTimeOut = @pobj._intSQLTimeOut;
	}
}

    จะเห็นได้ว่า มี Methods 2 ตัวที่ซ้ำกับ Class 2 classes ที่ผ่านมา นั่นคือ CopyMomento() และ Copy Constructor จากนั้นเราจะได้เห็น Method ที่ชื่อว่า SaveState() และ RestoreState() ซึ่ง มัน Inherits ลงไปเป็นชั้นๆ เพื่อให้ เราสามารถเรียกใช้งานได้

    ยังจำวิธีแรกได้ไหมครับ ที่ต้องมีตัวแปรอีกชุดหนึ่ง ที่เอาไว้เก็บสถานะของตัวแปรแต่ละตัว แล้วมันอยู่ตรงไหน ตัวแปรที่ว่านั้น ก็คือ ตัวแปรที่ชื่อว่า _momento นั่นเอง ตัวแปร Momento มีชนิดเป็น Persistent Object คุณอาจสงสัยว่า อย่างเก่งมันก็มีตัวแปรทั้งหมดของ PersistentObject ไม่มีของ Employee และ Secretary จริงครับ แต่เรื่องนี้มี Trick เล็กน้อยครับ ลองดูใน Method SaveState() ดูครับ คุณจะเห็นได้ว่า มีการใช้ตัวแปร this ถ้าผมถามว่า ตัวแปร this ในตัวอย่างนี้คือชนิดอะไร ไม่ใช่ PersistentObject นะครับ แต่หากเป็น Secretary ผู้ที่เรียกใช้ SaveState() ในที่นี้คือ Secretary นั่นเอง

    ผมใช้ลูกเล่นในการสร้าง Object ใหม่ โดยใช้ Activator มันจะสร้าง Object ตัวใหม่ ผมส่ง Parameter เข้าไป คือ this นั้นคือมันจะเรียก Copy Constructor ของ Secretary  ซึ่งในที่นี้ Object _momento เป็น เราเอาค่าของ Object จริงไปใส่เก็บไว้ ดังนั้น มันจะถูกเรียกเก็บข้อมูลเป็นลำดับชั้น เพราะใน Code ของ CopyMomento() นั่นเรียกใช้งาน Base Class ด้วย มันจะถูกทำงานเป็นลำดับชั้น ข้อมูลทุกตัวจึงมารวมตัวกันใน Object _momento นี้เอง

    เมื่อข้อมูลถูกรวบเก็บไว้ใน _momento เรียบร้อยแล้ว คราวนี้เรามาลองดูถึงวิธีการเอาค่ากลับ เมื่อต้อง RollBack การทำงานก็จะทำได้โดยการเรียกใช้ RestoreState() นั่นเอง ซึ่งมีขั้นตอนดังนี้ครับ

    ใน RestoreState() มีการเรียกใช้ CopyMomento() ซึ่งตามตัวอย่างนี้ ผู้เรียกคือ object ของ Secretary ดังนั้น มันจึงถูกกลไกของ Polymorphism ทำให้ CopyMomento() ของ class Secretary ถูกเรียกใช้ และ Parameter ที่ส่งนั่นก็คือ _momento นั่นเอง ดังนั้นมันจึงเป็น ถ่ายข้อมูลจาก _momento ไปเก็บเอาไว้ที่ตัว Object ที่ใช้งานนั่นเอง จากนั้น มันก็ถูกเรียกใช้งานเป็นชั้นๆ เช่นเดียวกัน เพื่อเติมข้อมูลให้สำเร็จในทุกๆ ส่วน

    จะเห็นได้ว่า Method CopyMomento() นั้น เป็นกลไกสำคัญในการคัดลอก ทั้ง ในการ SaveState() และ RestoreState() และภายในยังมีกลไกเพื่อให้รองรับ Inheritance อีกด้วย

    ผมพยายามทำให้ Code ของ Business Class เบาที่สุดจะได้ไม่ต้องมารับภาระ ที่ผมคาดไว้ ในงานของผมคงมี Business Class อยู่หลายร้อยตัว ผมเลยรีดเอาความซับซ้อนทั้งหมดไปเก็บไว้ใน PersistentObject ก็มันทำแค่ครั้งเดียวครับ

ทิ้งท้าย

    Pattern ตัวนี้ อยู่ในขั้นทดลองเท่านั้น ยังไม่ได้วิเคราะห์ให้ชัดเจนว่ามีจุดบกพร่องตรงไหน ผมยังไม่แน่ใจด้วยซ้ำไปว่า Pattern ตัวนี้มันจะไปอยู่ใน Framework ตัวใหม่หรือไม่ ถ้าใครมีอะไรแนะนำ ก็เขียนมาได้เลยครับ จะได้ปรับปรุงให้มันดีขึ้น