Archive

Archive for the ‘OOP’ Category

UML for C# programmers [ UML สำหรับ C# โปรแกรมเมอร์ ] ตอน 1

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

สิ่งที่เราต้องการพูดถึง ก็คือ การทำงานกับ UML Diagram และสื่อสาร กันระหว่าง นักวิเคราะห์ระบบ กับโปรแกรมเมอ์ (หรือ นักเขียนรหัสโปรแกรม ฮิ ฮิ ตั้งชื่อให้มัน โก้ ๆ ) หลายคน เห็นคำพูดนี้ก็จะร้องว่า เออ ใช่ แล้วไงหล่ะ ก็ นักวิเคราะห์ระบบ วิเคราะห์ เสร็จก็จะเขียน UML diagram เช่น class diagram ซึ่งมันเป็น conceptual level มันไม่ได้ให้ความหมายใด ใด เกี่ยวกับ ข้อมูล หรือโปรแกรม เช่น ตัวอย่าง diagram ข้างล่าง

   ในความหมายทาง OOP ของเรา ก็หมายถึง Dog is animal หรือ หมาเป็นสัตว์ ครับ diagram บอกแค่นี้แหละครับ แต่ในรายละเอียดของ Implementation level นั้น มันจะต้องถอดรหัสออกมา ให้แม่นยำมาก เพื่อให้ได้ตามความหมายที่ต้องการ ในที่นี้ ก็จะได้ออก มาเป็น  ( ท่านควรจะมีพื้นฐาน OOP ก่อน ที่ท่านจะมาอ่านเรื่องนี้นะครับ ขอบอก)

  public class Animal { … }
  public class Dog : Animal { … }

ตาม code อธิบายได้ว่า มี class Animal และ class Dog โดยที่  class Dog สืบทอดมากจาก Animal ก็หมาย ฟามว่า Dog มีคุณสมบัติทุกอยาก รวมถึงพฤติกรรมเหมือน Animal และก็จะต้องคุณสมบัติบางอย่างหรือ พฤติกรรมบางอย่างที่บ่งบอก ว่ามันเป็น Dog ครับ นั่นนะซิ ( เช่น หมาต้องกิน ขี้ หรือป่าว ฮิ ฮิ อันนี้ไร้สาระ แล้ว)

ก็จะสรุปว่า Diagram นั้น มันเป็น conceptual level ครับ มันสามารถเป็นปัญหาใหญ่ได้หาก โปรแกรมเมอร์ ถอดรหัสออกมาเป็น code ได้ไม่ถูกต้อง

ในที่นี้ก็เลยอยากจะ แสดง การถอดรหัส จาก diagram ไปเป็น code ให้เพื่อน ๆ ได้ติดตามอ่าน กันก็แล้วกันครับ

สำหรับ UML diagram ที่เราจะกล่าวถึง ก็จะเป็น ตัวที่เราใช้ในการ วิเคราะห์ระบบ หลัก ๆ สวนที่ไม่ได้กล่าวถึง นั้น ก็ไม่ได้กล่าวถึง ฮิ ฮิ
UML นั้นมีอยู่ 3 กลุ่ม diagrams นะครับ ประกอบด้วย

  • Static diagram อธิบาย โครงสร้างที่มันไม่เป็นแปลงหรือเคลื่อนไหว โดยใช้ class โครงสร้างของข้อมูล รวมถึงความสัมพันธ์ระหว่าง กัน
  • Dynamic diagram อธิบาย การทำงานของ entities ต่าง ๆ ในระหว่างการประมวลผล โดยการแสดงภาพของ flow การประมวลผล
  • Physical diagrams อธิบายโครงสร้างที่ ไม่มีการเปลี่ยนแปลง ในภาพของ sources files  หรือ libraries และความสัมพันธ์ที่เกิดขึ้น ระหว่าง กัน ครับ

ลองมาดูตัวอย่างกันนะครับ ด้านล่างแสดง code การทำ binary trees แบบหนึ่ง ดูให้เข้าใจแล้วค่อยไปดู ไปไล่ดู diagram กันนะครับ

public class TreeMap{
  TreeMapNode topNode = null;
  public void add(Comparable key, Object value)
  {
    If(topNode == null)
      topNode = new TreeMapNode(key, value)
    else
      topNode.add(key,value)
   }

  public Object get(Comparable key)
  {
    return topNode == null ? null : topNode.find(key) ;
  }
}

class TreeMapNode {

  private final static int LESS = 0;
  private final static int GREATER = 1;
  private Comparable itsKey;
  private Object itsValue;
  private TreeMapNode nodes [] = new TreeMapNode[2]

  public TreeMapNode(Comprarable key, Object value)
  {
     itsKey = key;
     itsValue = value;
  }

  public Object find(Comparable key)
  {
     if(key.compareTo(itsKey) == 0) return itsValue;
     return findSubNodeForkey(selectSubNode(key),key);
  }

   public int selectSubNodeForKey(int node, Comparable key) {
     return node[nodes] == null ? null : node[node].find(key) ;
   }

  public void add(Comparable key, Object vlaue)
  {
     if(key.compareTo(itskey) == 0)
        itsValue = value;
     else
        addSubNode(selectSubNode(key),key,value);
   }

   public void addSubNode( int node, Comparable key, Object value){
      if( nodes[node] == null)
         nodes[node] = new TreeMap(key, vlaue);
      else
         nodes[node].add(key,value);
    }
}

เอาละครับ คราวนี้เรามาดูกันที่ diagram ว่าจะเป็น อย่างไรกันบ้าง

Class Diagram

      class diagram จะแสดง class หลัก และความสัมพันธ์ของ class เหล่านั้น ครับ จากตัวอย่างโปรแกรม นะครับ เราจะเห็น ว่ามี class อยู่ 2 class  คือ TreeMap และ TreeMapNode  กล่าวคือ class TreeMap นั้น มีความสัมพันธ์เชื่อมโยง ไปยัง TreeeMapNode ในลักษณะที่ TreeMapNode เป็น member ของ TreeMap  ในตัวแปรที่ชื่อว่า topNode  ในขณะเดียวกัน TreeMapNode ก็ อ้างอิงไปที่ TreeMapNode ก็คือตัวเอง ลองมาดูว่าในลักษณะนี้เราจะเขียน class diagram และ relation ระหว่างกันอย่าไร

จาก Code นั้นก็จะได้อย่าง diagram ที่เห็น ด้านบนนี้ นะครับ จากรูป สิ่งที่เราควรจะทราบก็คือ

  • กล่องสี่เหลี่ยม จะแทน class ชื่อ class จะอยู่ด้านบนสุด ตามด้วย attribute และ method อยู่ล่างสุด
  • ใน diagram relationship ทั้งหมด เป็น association ซึ่งเป็น relation ship แบบง่ายสุด ใช้การสร้างตัวแปล object เพื่อที่จะ อ้างอิง และเรียกใช้ method ของ class ที่ต้องการ
  • ชื่อที่กำกับ association เป็นชื่อ ตัวแปร object ของ class  ที่ต้องการอ้างอิง
  • ตัวเลขที่กำกับ เป็นตัวเลขบอก จำนวน instance ที่ relationship สามารถอ้างอิง ได้

Object diagram

  ภาพด้านล่างเป็นการแสดง Object Diagram และ relation ship ในเวลาใดเวลาหนึ่งของการประมวลผล หรือเราอาจเรียกได้ว่าเป็นการทำ snapshot ของ memory มาแสดง นั่นเอง

จากภาพ รูปสี่เหลี่ยมแทน Object นะครับ จะสังเกตุเห็นว่า เป็น ชื่อ class จะขีดเส้นใต้นะครับ  ซึ่งเป็น class ที่ เป็น object นั้น ๆ ด้านล่างของ object แทน ตัวแปร object ซึ่งจะเห็นว่าบ้างตัวจะถูกกำหนดค่าให้  และ  relation ระหว่าง object เราเรียกว่า link ซึ่งเป็นตัวเดียวกับ association ครับ

Sequence Diagrams

    สำหรับ sequence diagram นั้น จะเป็นการแสดง ว่า method ต่าง ๆ นั้น implement อย่างไร

จากรูปจะเห็น ว่า เริ่มแรกจะต้องมีการ เรียก add() method ของ TreeMap ถ้า topNode มีค่าเป็น null TreeMap จะresponse ด้วย การสร้าง TreeMapNode ใหม่ แล้วกำหนดให้ topNode ถ้า topNode มีค่าไม่เท่ากีบ null TreeMap จะเรียก add() method ของ topNode

นิพจน์ที่อยู่ในวงเล็บ สี่เหลี่ยม เรียกว่า guardห สำหรับการแสดง ทางเลือกกำกับ   และ หัวลูกศรที่จบที่  TreeMapNode icon นั้นแทน construction  และ bar สี่เหลี่ยม ใต้ Treemap เรียกว่า activation ใช้แสดงห้วงเวลาที่ add method ทำงาน

Collaboration Diagram

      Diagram ต่อไปเรียกว่า collaboration Diagram ซึ่งเป็น diagram ที่แสดงข้อมูลคล้ายกันกับ Sequence diagram  กล่าวคือ sequence diagram นั้นเป็นตัวแสดงให้เห็นลำดับการทำงานของ การเรียก message แต่ใน collaboration diagram นั้นจะแสดงให้เห็น ความสัมพันธ์ระหว่าง object ดังรูป

จากรูป object ถูกเชื่อมเข้าด้วยกัน ด้วย สัญลักษณ์ที่เรียกว่า link ดังนั้น link จะเกิดขึ้นได้ก็ต่อเมื่อ object หนึ่งสามารถ ส่ง message ถึง อีก object หนึ่งได้  สัญลักษณ์ หัวลูกศร คู่กับ message จะกำกับอยู่กับ link แสดงให้เห็น ชื่อ message และทิศทางที่เรียก หมายเลข ด้านหน้าและมี dot นั้นแสดงให้เห็น ลำดับของการเรียก เป็นชั้น

จะเห็นว่า TreeMap.add Function (message 1)  เรียก TreeMapNode.add Function (message 1.1)  พูดได้อีกอย่างหนึ่งว่า message 1.1 นั้น เป็น message แรกที่ ที่เรียกจาก message 1 ครับ

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

s_teerapong2000@yahoo.com

Teerapong Sontayaman

ธีระพงษ์ สนธยามาลย์

Categories: C# .NET, OOP

NHibernate Why ???

January 11, 2011 1 comment

เป็นคำถามที่แรกที่หลายคนถามว่า ทำไมถึงต้อง NHibernate  สำหรับผู้พัฒนาระบบ ที่ทำงานมายาวนานแล้ว พอที่จะรู้คำตอบในใจอยู่แล้วว่า การเขียน code เพื่อจัดการกับ object persistence นั้นเป็นเรื่องที่สร้างความ เหน็ดเหนื่อยให้ผู้พัฒนาเป็นอย่างมาก ผมก็เห็นด้วย เพราะงานในการอ่านข้อมูลจากฐานข้อมูล มาสร้างเป็น Object และบันทึกข้อมูลที่เป็น Object กลับลงฐานข้อมูลนั้น ใช้เวลา มากพอสมควร บางท่านบอกว่า อาจต้องใช้ถึง 1 ใน 4 ของ เวลาที่ต้อง coding ทั้งหมด [ผมหมายถึงเวลาที่เราเขียน n-tier aplication นะครับ] และงานพวกนี้ก็ ทำซ้ำซาก ใช้เวลา น่าเบื่อเป็นที่สุด

ครับได้มีการแก้ปัญหานี้กันอย่างกว้างขวาง อย่างเช่น การทำ code generator ให้กับ data acces layer สามารถสร้าง code ให้ได้ภายใน พริบตาเดียวแต่ปัญหาก็ยังคงอยู่เนื่องจาก หากมีการแก้ไขโครงสร้างฐานข้อมูลเมื่อใด ก็จำเป็นจะต้องมีการ re – create ใหม่อีก

สำหรับ Object-relational managers หรือ ORM อย่างเจ้า NHibernate นั้นแตกต่างครับ ท่าน  มันสามารถจัดการกับ data acces ได้อย่างมีประสิทธิภาพ และสามารถเข้าถึงข้อมูลได้ง่าย เพียงไม่กี่บรรทัด

แนะนำ NHibernate

NHibernate เป็น เป็น persistence engine ในรูปของ Framworke  เอาว่ามันทำหน้าที่ load และ สร้าง business object จากฐานข้อมูล และบันทึกการเปลี่ยนแปลงจาก object กลับลงฐานข้อมูล ครับ และสามารถทำง่ายดังพริกฝ่ามือ เติมให้อีกนิด

เจ้า NHibernate มันจะใช้ข้อมูลใน mapping files [ XML ] สำหรับการแปลงจาก ข้อมูลในฐานข้อมูล เป็น Objects [ ไอ้เจ้า Object ที่สัมพันธ์กับ table ในฐานข้อมูลนี่แหละที่เรียกว่า persistence object ]


 

 

 

 

 

 

 

 

 

ดังนั้นทำให้เราแค่ เพิ่ม code สองสามบรรทัด และสร้าง mapping file ให้กับ persistent class [ class ที่สัมพันธ์กับ table ในฐานข้อมูล ] ที่เหลือ เช่น การจัดการกับข้อมูลทั้งหมด   NHibernate จะจัดการให้ สุดยอดไหมหล่ะครับ save เวลาไปเยอะนะครับ

ครับ NHibernate ไม่ใช่ ORM framework ที่มีอยู่เพียงอันเดียว ที่มีอยู่ ในโลกของ .NET นะครับ  มีตัวอื่น ๆอีกมากมาย ไม่ว่าจะเป็น Open source หรือ commercial ที่ทำงานในลักษณะเดียวกันครับ แต่เจ้า NHibernate เป็นตัวหนึ่งที่ค่อนข้างเป็นที่นิยมกันครับ อาจจะเป็นเพราะว่ามันมี บรรพบุรูษเป็น Hibernate ซึ่งเป็น ORM  Framework ยอดนิยมของ Java ก็เป็นได้ นอกจากนั้น Microsoft สัญญาว่าจะมี Entity Framework สำหรับ ADO.NET ที่มีความสามารถเป็น ORM แต่มันก็ยังไม่คลอดออกว่า คงต้องรอก็ต่อไปสักพักนะครับ

ครับ สำหรับเรื่องของ NHibernate นั้นคงต้องว่ากันต่อไป นะครับส่วนท่าที่ต้องการจะอ่านเพิ่มเติมก็สามารถไปตาม link นี้นะครับ อ่านเพิ่มเติมได้ที่

ของผมเองก็มีเหมือนกันะครับ  สะพานเชื่อมระหว่าง Object และ Database

s_teerapong2000@yahoo.com

NHibernate สะพานเชื่อมระหว่าง Object world และ Database world

January 10, 2011 2 comments

นักพัฒนาทั้งหลายครับ สวัสดีปีใหม่ครับหวังว่าปีนี้คงจะมีอะไรที่ ใหม่ ๆ กับชีวิตนะครับ เงินทองหลั่งไหล สุภาพแข็งแรงนะครับ นับว่าเป็นเวลานานมากแล้วที่ผมไม่ได้เข้ามาเขียนบทความเลย ปีใหม่นี้ก็หวังว่าจะเริ่มเขียนให้มากขึ้น อยากจะแบ่งปัน ความรู้ให้กับเพื่อน ๆ โปรแกรมเมอร์ทั้งหลาย นะครับ

เรื่องในวันนี้นับเป็นเรื่องแรกแห่งปีนี้ ครับ  อย่างที่ได้ขึ้นหัวไว้นะครับ  NHibernate ถือว่าเป็น นวัตกรรม ของการพัฒนาซอฟต์แวร์ แห่งปีก็ว่าได้นะครับ นักพัฒนาระบบอย่างเรา คงจะเข้าใจได้นะครับ ว่าระบบฐานข้อมูลส่วนใหญ่ที่เราใช้งานั้น จะเป็น relational database ซะส่วนใหญ่ และการพัฒนาระบบในภาษาใหม่นั้น เราก็จะเน้นการเขียน แบบ Object oriented  เมื่อเราทำงานในลักษณะ Object เราก็น่าจะสามารถมองเห็น ข้อมูลเป็น Object ด้วย แต่การ ทำ Data access Layer นั้น เป็นงานหนักทีเดียว ถ้าต้องการทำให้ การเขียนเป็น เชิง Object ทั้งหมด ทั้งนี้ผมกล่าวไปตามความเข้าใจ นะครับ และก็เข้าใจว่า NHibernate เป็นหนทางที่จะนำเราไปสู่การส้รางงานที่ง่ายขึ้น แต่จะเป็นอย่างไรนั้นคงจะต้องหาคำตอบ ไปด้วยกันนะครับ

คงจะต้องกล่าวถึง NHibernate ก่อนที่จะลองสัมผัส กันสักนิด นะครับ สำหรับ NHibernate นี่นั้นเขาเรียกกันว่า Object Relational Mapper (ORM) ทำหน้าที่ Map จาก Relational database มาเป็น Object ให้เราครับ ทำให้เราทำงานได้ง่ายขึ้นครับเพราะเราไม่ต้องไปงุ่มง่ามอยู่กับการเขียน Data access layer  ง่ายอย่างไรนั้นคงต้อง ค่อย ๆ ดูตามผมไปก็แล้วกันนะครับ เพราะผมเองก็อยากรู้มากเหมือนกัน

เอาเป็นว่าเราเรียนรู้ NHibernate ผ่านทางต้วอย่างที่ผมจะรวบรวมมา จากง่ายไปหาอยากและเอาไปใช้งานจริง ๆ นะครับ

เครื่องมือที่เราจะใช้ใน ตัวอย่างแรกนี้ก็คงจะเป็น

  • NHibernate version 2.1.2 ครับ load ตาม link นี้เลยครับ NHibernate-2.1.2.GA-bin
  • Visual studio 2008 Express, C#
  • SQL Server 2005 Express

ขั้นตอนการติดตั้ง NHibernate ก็ไม่ยากนะครับ ผมแตกออกแล้วเอาไว้ที่ c:\NHibernate-2.1.2 GA\ แล้ว ทำตามขั้นตอนต่อไปนี้นะครับ

ขั้นตอนที่ 1 :

  • สร้าง Project ชื่อ Lesson3 แล้วสร้าง form แล้ววาง datagrideview ไว้ บน form นั้น นะครับแล้วทิ้งไว้ก่อน
  • Add Reference -> NHibernate
    • Click ขวาที่ชื่อ Project เลือก Add Reference
    • Brows ไปยัง folder ที่แตกไว้แล้วเลือก NHibernate.dll ใน \Required_Bins
    • ทำอีกครั้ง เพื่อ Add    NHibernate.ByteCode.Castle.dll  ใน \Required_For_LazyLoading\Castle

ขั้นตอนที่ 2 :

  • Add new class -> company.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Lesson3
{
 public class company
 {
 public company() { }

 public virtual string companyid { get; set; }
 public virtual string companyname { get; set; }
 public virtual string companyemail { get; set; }
 public virtual string companysize { get; set; }

 }
}
  • Add new XML ->    Set as Embedded Resource [company.hbm.xml]
  • สำคัญมากสำหรับ ไฟล์นี้ ต้องกำหนด properties ให้เป็น  embedded resource
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Lesson3" auto-import="true">
 <class name="Lesson3.company, Lesson3" table="company" lazy="true">
 <id name="companyid" type="string">
 <generator/>
 </id>
 <property name="companyname" type="string"/>
 <property name="companyemail" type="string"/>
 <property name="companysize" type="string"/>

 </class>

</hibernate-mapping>

ขั้นตอนที่ 3 :

  • config and Execute
    • Add using statment , เปิด form1 ที่เราวาง datagridview ไว้แล้วเพิ่ม using statement ตามนี้ครับ
...
using NHibernate;
using NHibernate.Cfg;
using NHibernate.ByteCode.Castle;
using System.Collections;

  • แล้ว เพิ่ม code ลงใน constructor ดังนี้
Configuration config = new Configuration();
ISessionFactory factory;

 config.SetProperty(NHibernate.Cfg.Environment.ConnectionProvider, "NHibernate.Connection.DriverConnectionProvider");
 config.SetProperty(NHibernate.Cfg.Environment.Dialect, "NHibernate.Dialect.MsSql2005Dialect");
 config.SetProperty(NHibernate.Cfg.Environment.ConnectionDriver, "NHibernate.Driver.SqlClientDriver");
 config.SetProperty(NHibernate.Cfg.Environment.ConnectionString, @"Data Source=.\SQLEXPRESS;Initial Catalog=enterprise4;User ID=sa;Password=xxxx;Pooling=False");
 config.SetProperty(NHibernate.Cfg.Environment.ProxyFactoryFactoryClass, "NHibernate.ByteCode.Castle.ProxyFactoryFactory,NHibernate.ByteCode.Castle");

 config.AddAssembly(typeof(Lesson3.company).Assembly);

 factory = config.BuildSessionFactory();
 ISession session = null;
 try
 {
 session = factory.OpenSession();
 dataGridView1.DataSource = session.CreateCriteria(typeof(Lesson3.company)).List();
 }
 catch (Exception ex)
 {
 session.Close();
 MessageBox.Show(ex.Message);
 //throw;
 }

ลืมไปว่าท่านต้องทำการ สร้าง database ชื่อ enterprise4 โดยที่มี โครงส้รางข้อมูลดังนี้

CREATE TABLE [dbo].[company](
[companyid] [nchar](10) NOT NULL,
[companyname] [nchar](150) NULL,
[companyemail] [nchar](150) NULL,
[companysize] [nchar](10) NULL,
CONSTRAINT [PK_company] PRIMARY KEY CLUSTERED
(
[companyid] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

หลังจากนี้ก็ลอง compile แล้วrun ดูนะครับ จะได้ข้อมูลขึ้นมาแสดง ให้ท่านเห็น อ้อ แต่อย่าลืม ใส่ข้อมูลลงตารางซะก่อนนะครับ
จาก เห็นมั้ยครับ ว่ามันจัดการอะไรให้เราบ้างเราแค่เขียน แค่นี้เราก็สามารถที่จะแสดงข้อมูลจาก table ได้แล้ว ดังนั้น ต้องติดตามกันต่อไปนะครับว่า การทำ operation ต่าง ๆ กับข้อมูลทำได้อย่างไรบ้าง
เช่นการ insert update และการ delete ซึ่งจะเก็บไว้เป็นตอนต่อไป ครับ ถ้ามีอะไรที่ต้องการ ถามก่อนก็ ว่ากันไปตามนี้นะครับ s_teerapong2000@yahoo.com ครับ Bye…

ประยุกค์ใช้ Template patterns และ Generics เพื่อสร้าง Data Access Layer

January 17, 2010 2 comments

ในบทความนี้แสดง การทำงานร่วมกัน ของ  template pattern และ  Generics ของ .Net framework  เพื่อสร้าง Data Access Layer …..

เพื่อความเข้าใจ ขออธิบาย template pattern ก่อนนะครับ   สำหรับ template pattern นั้น จุดประสงค์ของการใช้งานนั้นคือ เพื่อที่จะแบ่ง code ที่ต้องการให้ เปลี่ยน กับ code ที่ไม่ต้องการให้เปลี่ยน    ตัว template pattern นั้นใช้สำหรับ code ที่คงที่ และถูกเรียกใช้งานบ่อยใน class  ดังนั้นหลากเราพบว่าต้องเขียน code เดิม ๆ บ่อยครั้ง เราสามารถลดงานตรงนั้นได้ด้วย template pattern.

เพื่อให้เห็นภาพ เราลองมาดูตัวอย่างกันนะครับ  ลักษณะของงานที่จะต้องใช้ template pattern ดังที่พูดไปก็คือ มี class หลาย class ที่มี การทำงาน คล้ายกัน หรือ มีลักษณะของ พฤติกรรมคล้าย ๆ กัน  ครับตัวอย่างของเราก็คือ  GetTheFancyNumber()

// DoSometing class
Public class DoSomething
{
   private int m_number;
   public int GetTheFancyNumber()
   {
     int  theNumber = m_number;
     // Do lats of crazy staff to theNumber here.
     Return theNumber;
   }

}

// DoSomethingElse class
Public class DoSomethingElse
 {
    private int m_number;
    public int GetTheFancyNumber()
    {
      int theNumber = m_number;
       // Do lots of crazy staff to theNumber Here.
       return theNumber;
    }
 }

ตัวอย่างพยายามแสดงให้เห็นว่า ทั้งสอง class นั้น มี การทำงาน เหมือนกัน เราสามารถที่นำ code นั้นมาใส่ไว้ใน base class และ ส่วนที่ต่างกันยกให้เป็นหน้าที่ของ class ลูก หรือ class ที่สืบทอดไปจัดการต่อ จากในตัวอย่างของเรานั้น เราจะนำ “GetTheFancyNumber()” method ใส่ไว้ใน base class และทำการ encapsulate สำหรับการกำหนดค่าให้กับ ตัวแปร theNumber ให้ class ที่สืบทอดไป ดำเนินการเอง ครับ ดังนั้น code ที่เราจะได้สำหรับการสร้าง base class จะได้ดังนี้

Public abstract class DoSomethingBase
{
   protected abstract int GetTheNumber();
   public int GetTheFancyNumber()
   {
       int theNumber = GetTheNumber();
       // do something to theNumber here.
       return theNumber;
    }
}

เราสร้าง child class เราใช้ logic ที่ encapsulated ด้วย template ( GetFancyNumber()) และทำการ implement ในส่วนที่ class ลูก อาจจะใช้วิธีการหาตัวเลขแตกต่างกัน ก็คือ GetTheNumber() ครับ ดังตัวอย่าง

Public class DoSomethingElse : DoSomethingBase
{
    private int m_number;
    protected override int GetTheNumber()
    {
        return m_number;
    }
}
Public class DoSomething : DoSomethingBas
{
    private int m_number;
    protected override int GetTheNumber()
    {
         return m_number;
    }
}

ครับ ในส่วนของ template pattern นี้เราคงพอได้ concept แล้วนะครับ สำหรับ template pattern นี้หากเรานำมาใช้งานร่วมกับ generics แล้วจะได้ประสิทธิภาพมาก ซึ่งจะได้ดูในตัวอย่างต่อไป ครับ

ในส่วนต่อไปเมื่อเข้าใจ การประยุกค์ใช้ template design pattern แล้ว เราจะนำ template นี้มาทำงานร่วมกับ generics ครับ
ในเรื่องของ การทำงานกับ database นั้น มีอยู่งานหนึ่งที่เราต้องทำ ทุกครั้งคือ การเข้าถึง database แล้วดึงข้อมูลออกจาก database และ สร้าง object ของข้อมูลนั้นเพื่อนำมาใช้งานต่อไป ส่วนของงานดังกล่าวนี้ ก็คือ DAL (Data access layer ) ครับ ซึ่งเราจะลองมา implement ด้วย template pattern. กันครับ
สมมุติว่าเรามี table ที่ชื่อว่า Person ดังนี้ครับ
Create table [tblPerson](
[PersonId] [int] IDENTITY (1,1) NOT NULL,
[FirstName] [Nvarchar] (50),
[LastName] [Nvarchar] (50),
[Email] [Nvarchar] (50),
CONSTRAINT [PK_tblPerson] PRIMARY KEY CLUSTERED
([PersonId]) ON [PRIMARY]
) ON [PRIMARY]
และเราสร้าง class สำหรับการเก็บข้อมูล Person ในแต่ละ record ดังนี้ครับ

 public class Person
{
     string m_email;
     string m_firstname;
     string m_lastname;
     int m_id;

     Persion()
     {
     }
     public string Email
      {
           get{ return m_email;}
           set{ m_email = value;}
      }
     public string FirstName
     {
           get{ return m_firstname; }
           set{ m_firstname = value; }
     }
    public string LastName
    {
          get{return m_lastname;}
          set{m_lastname = value;}
    }
    public int Id
    {
         get{return m_id;}
         set{m_id = value; }
    }
}

ครับ ในการ access database มีอยู่ 2 สิ่งที่เราต้องทำ เสมอคือ
1 access database – run คำสั่ง (command) – get result
2 เรา นำข้อมูลที่ได้ (result) มาสร้างเป็น object
ทั้งสอง ขั้นตอนนี้ เหมาะสำหรับการ implement ด้วย template pattern นะครับ เราลองมาดูที่ ข้อ 2 ก่อนนะครับ เราเรียกมันว่า mapping
เราจะทำการสร้าง Base class ที่มีหน้าที่ในการ map ข้อมูลที่ได้จากการ ประมวลผล command มาสร้างเป็น object ของข้อมูลนั้น และนำมาใส่ไว้ใน collection นะครับ เราจะสร้าง base class ที่เป็น generic นะครับมีหน้าตอดังนี้

Abstract class MapperBase<T>
{
     protected abstract T Map(IDataRecord record);
     public Collection<T> MapAll(IDataReader reader)
     {
        Collection <T> collection = new Collection<T>();
        while(reader.Read())
        {
             try{
                collection.add(Map(reader));
             }catch{
                   throw;
             }
        }
        return collection;
     }
}

ต่อไปเราลองมาดูการ สืบทอด จาก MapperBase เพื่อสร้าง Person objects ดูนะครับ

    public class PersonMapper :MapperBase<Person>
    {
        protected override Person Map(IDataRecord record)
        {
            //throw new Exception("The method or operation is not implemented.");
            try
            {
                Person p = new Person();
                p.Id = (DBNull.Value == record["PersonId"]) ? 0 : (int)record["PersonId"];
                p.FirstName = (DBNull.Value == record["firstname"]) ? string.Empty : (string)record["firstname"];
                p.LastName = (DBNull.Value == record["lastname"]) ? string.Empty : (string)record["lastname"];
                p.Email = (DBNull.Value == record["email"]) ? string.Empty : (string)record["email"];

                return p;
            }
            catch (Exception)
            {

                throw;
            }
        }
    }

เป็นงัยครับ ง่ายไหม ครับการสร้าง mapper ให้กับ class ที่เราดึงข้อมูลมาจาก ตารางข้อมูล

ต่อไปเป็นการ ใช้ template pattern + generice เพื่อสร้าง data access
เรามาดูกันว่า งานอะไรบ้างที่ต้องมีการเปลี่ยนแปลงหรือ มีรายละเอียดของงานต่างกัน สำหรับการเข้าถึงแต่ละ table ครับ
1 การได้มาซึ่ง connection
2 การสร้าง sql command และ sql command type
3 และ การได้มาซึ่ง mapper (จากหัวข้อที่แล้ว)
ซึ่งเราจะกำหนด ให้เป็น abstract method ดังนี้

IDbConnection GetConnection();
string CommandText { get; }
CommandType CommandType { get; }
Collection GetParameters(IDbCommand command);
MapperBase GetMapper();

ตามที่ได้ทราบแล้วนะครับ methods ดังกล่าวนั้นจะต้องถูก overrided ด้วย class ใดก็ตามที่ สืบทอด base class ที่จะกล่าวต่อไป นี้
ส่วนงานที่เราสมมุติว่าต้องมีกระบวนการเหมือน เราก็ใส่ไว้ใน method ที่ชื่อว่า Execute() ซึ่งจะเป็นผู้สร้าง collection ของ object ที่เราต้องการ

    public abstract class ObjectReaderBase<T>
    {
        protected abstract IDbConnection GetConnection();
        public abstract string CommandText { get;}
        public abstract CommandType Commandtype { get;}
        protected abstract Collection<IDataParameter> Getparameters(IDbCommand command);
        protected abstract MapperBase<T> GetMapper();

        public Collection<T> Execute()
        {
            Collection<T> collection = new Collection<T>();
            using (IDbConnection connection = GetConnection())
            {
                IDbCommand command = connection.CreateCommand();
                command.CommandText = CommandText();
                command.CommandType = Commandtype();
                foreach (IDataParameter param in this.Getparameters(command))
                    command.Parameters.Add(param);

                try
                {
                    connection.Open();
                    using (IDataReader reader = command.ExecuteReader())
                    {
                        try
                        {
                            MapperBase<T> mapper = GetMapper();
                            collection = mapper.MapAll(reader);
                            return collection;
                        }
                        catch (Exception)
                        {

                            throw;
                        }
                        finally
                        {
                            reader.Close();
                        }
                    }
                }
                catch (Exception)
                {

                    throw;
                }
                finally {
                    connection.Close();
                }
            }
        }

    }

และต่อไปนี้เป็นตัวอย่างของ การใช้งาน ObjectReaderBase โดยเราจะทำการ สร้าง child class ชื่อ PersonReader ดังนี้

class PersonReader: ObjectReaderBase<Person>
{
    private static string m_connectionString =
         @"Data Source=DATA_SOURCE_NAME;Initial Catalog=Test;Integrated Security=True";

    protected override System.Data.IDbConnection GetConnection()
    {
        // update to get your connection here

        IDbConnection connection = new SqlConnection(m_connectionString);
        return connection;
    }
    protected override string CommandText
    {
        get { return "SELECT PersonID, FirstName, LastName, Email FROM tblPerson"; }
    }

    protected override CommandType CommandType
    {
        get { return System.Data.CommandType.Text; }
    }

    protected override Collection<IDataParameter> GetParameters(IDbCommand command)
    {
        Collection<IDataParameter> collection = new Collection<IDataParameter>();
        return collection;

        //// USE THIS IF YOU ACTUALLY HAVE PARAMETERS
        //IDataParameter param1 = command.CreateParameter();
        //param1.ParameterName = "paramName 1"; // put parameter name here
        //param1.Value = 5; // put value here;

        //collection.Add(param1);

        //return collection;
    }

    protected override MapperBase<Person> GetMapper()
    {
        MapperBase<Person> mapper = new PersonMapper();
        return mapper;
    }
}

ครับคงได้แนวความคิด สำหรับการใช้งาน template pattern บวกกับ generics นะครับ คงพอมี ideas สำหรับการนำไปปรับปรุงใช้งาน หรือนำไปเพิ่มเติมให้ได้ตามความต้องการใช้งาน นะครับ
ครับ เพื่อการนำไปใช้ ท่านก็คงต้องเพิ่มเติม วิธีการเข้าถึง database ในรูปแบบต่าง ๆ เช่น ExecuteNonquery() หรือ ExecuteScalar() ครับ สุดท้ายมาดูการ ใช้งานสักนิดนะครับ

static void Main(string[] args)
{
    PersonReader reader = new PersonReader();
    Collection<Person> people = reader.Execute();

    foreach (Person p in people)
        Console.WriteLine(string.Format("{0}, {1}: {2}",
            p.LastName, p.FirstName, p.Email));

    Console.ReadLine();

}
<pre>

ครับสิ่งหนึ่งที่เราได้ ประโยชน์จากการใช้งานรูปแบบนี้นั้น ก็คือเราสามารถลดงานที่ต้องเขียน code ลงไปมากเนื่องจาก เราได้ลดส่วนที่ต้องทำทุกครั้งเหมือน ๆ กัน แยกออกไป จากส่วนที่ ต้องทำงานแตกต่างกันในแต่ละครั้ง ทำให้การ บำรุงรักษา นั้นง่ายและสะดวก และลดจำนวน code ที่ต้องเขียนลงมากครับ
ธีระพงษ์ สนธยามาลย์ Soft Speed solution ‘s Senior Programmer , s_teerapong2000@yahoo.com
ที่มาของบท ความ An elegant C# Data Access Layer using the Template Pattern and Generics , By  Matthew Cochran May 22, 2006
และบทความที่ ใกล้เคียงครับ http://www.codeproject.com/KB/database/BusinessObjectHelper.aspx

Design patterns คำตอบของการแก้ปัญหาเชิง software architectures…

ผมเคยเขียนเกี่ยว design pattern ไปบ้างแล้วโดย ยกเอาบาง design pattern มาอธิบาย  และแสดงตัวอย่าง  เขียนเสร็จแล้ว อ่านเองก็ยังไม่ค่อยเข้าใจครับ ผมก็เลยคิดว่าจะหาที่ reference ดี ๆ ที่เขามีวิธีการอธิบายดีให้เราได้เข้าใจกันง่าย ๆ และมีตัวอย่างที่เห็นภาพการใช้งาน   และก็จะกล่าวถึงให้ครบถ้วนทุกตัวตราบเท่าที่ความขยันยังมีอยู่ ครับ

ท่านที่เป็น โปรแกรมเมอร์ทั้งหลายทราบดีอยู่แล้วว่า design pattern ก็คือ รูปแบบที่สามามารถนำมาใช้แก้ปัญหา ใน ปัญหา หลากหลายรูปแบบต่าง ๆ กัน ซึ่งรูปแบบดังกล่าวนี้  ได้ถูกปรับปรุงครั้งแล้วครั้งเล่าจน ได้ รูปแบบต่าง ๆ ออก มาเป็น design pattern ในแต่ละชนิด ใช้แก้ปัญหา ในแต่ละแบบต่าง กันไปตามความเหมาะสม    (หรือ ความหมายตาม วิกิพีเดีย)

กล่าวกันว่าถ้าเราสามารถทำความเข้าใจ design pattern แต่ละตัวอย่างลึกซึ่งแล้ว ท่านจะสามารถนำไปใช้พัฒนา การออกแบบ สถาปัตยกรรม software  ให้มีความ ทนทานและมีความยืดหยุ่น สูง  ง่ายต่อการ บำรุงรักษา ง่ายต่อการปรับปรุง

Gang of Four (GOF) patterns นั้นถือว่าเป็นพื้นฐานของ design patterns อื่น ๆ ทั้งหมด  ได้มีการแยกแยะออกเป็น กลุ่ม ได้ 3 กลุ่ม คือ   Creational  Structural และ Behavioral ซึ่งผมจะกล่าวถึง design pattern หมดในแต่ละกลุ่มโดยละเอียด – ดังนี้

Creational Patterns
– Abstract Factory
– Builder
Factory Method
– Prototype
– Singleton
Structural Patterns
– Adapter
– Bridge
– Composite
– Decorator
– Facade
– Flyweight
– Proxy
Behavioral Patterns
– Chain of Resp.
– Command
– Interpreter
– Iterator
– Mediator
– Memento
– Observer
– State
– Strategy
– Template Method
– Visitor

Teerapong Sontayaman , Soft  Speed solution – senior programmer  s_teerapong2000@yahoo.com

Categories: OOP, software engineering

Creating and Using a Static Library (C++) การสร้างและการใช้งาน static library ….

การสร้างและการใช้งาน static library ….

คงไม่ต้อง ให้คำอธิบาย นะครับ ว่า static library คืออะไร เพราะ ถ้าเราหาเรื่องนี้อ่าน ก็หมายถึงว่าเราอยากรู้ว่ามันเป็นอย่างไร ใช้งานอย่างไร  ครับ การใช้ static library นั้นเป็นหนทางที่ดีในการ reuse code หรือ การเขียนโปรแกรมแบบที่สามารถนำส่วนของ code ที่ใช้บ่อย ๆ  มารวมกันไว้ที่ใดที่หนึ่ง ให้สามารถถูกเรียกใช้ได้ ทั่วไป โดยไม่ต้อง ไปเขียน code ลักษณะเดียวกัน ทุกครั้งที่ต้องการใช้งาน  ไหนบอกว่าจะไม่อธิบายไง    นั่นก็คือ เขียนครั้งเดียว ใช้ได้ พร่ำเพรื่อ ฮะ ฮะ

ในบทความนี้ก็จะกล่าวถึง

  • การสร้าง static library
  • การเพิ่ม class ลงใน static library
  • การ สร้าง app ที่เรียกใช้ หรือ อ้างอิง function หรือ class ใน static library

ก่อนอื่นต้อง บอกก่อนว่า ผมอ้างอิง visual c++ ของ Microsoft นะครับ อาจจะเริ่มตั้งแต่  visual studio .NET ขึ้นไปนะครับ

การสร้าง static library

ในการสร้าง static library นั้นเรา ทำตาม ขั้นตอน เหล่านี้นะครับ

  • สร้าง  Project ใหม่       [เลือก New -> Project ]
  • กำหนด ให้ Project type เป็น Win32 [visual c++ ->  Win32 เลือก console application ]
  • กำหนดชื่อ project ให้เป็น MatchFuncLib หรือ ชื่ออื่นก็ได้ตามที่ต้องการ และกำหนด solution Name ให้เป็น  StaticLibrary  หลังจากนั้น ก็ click OK
  • click Next เมื่อ เจอหน้า Win32 Application Wizard
  • กำหนด Application type ให้เป็น static library
  • และเอา Precompiled header ออกpic2
  • สุดท้าย Click finish เพื่อจบขึ้นตอนการสร้าง Project

การเพิ่ม class ลงใน static library

  • สร้าง Header file ให้กับ class ใหม่
  • ที่ Project menu ,  เลือก Add New Item หลังจาก Add New Item dialog box แสดงขึ้นมาให้  เลือก code และ Header File(.h) กำหนดชื่อ ให้เป็น MathFuncsLib.h แล้ว กดปุ่ม Add  จะได้ file เปล่าๆ ขึ้นมาหนึ่ง file
  • เพิ่ม  code นี้ลงไป code นี้ผมเอามาจาก  http://msdn.microsoft.com/en-us/library/ms235627.aspx นะครับ หากต้องการ ข้อมูลเพิ่มเติม ให้เป็นตาม link ได้เลย

namespace MathFuncs
{
class MyMathFuncs
{
public:
// Returns a + b
static double Add(double a, double b);

// Returns a – b
static double Subtract(double a, double b);

// Returns a * b
static double Multiply(double a, double b);

// Returns a / b
// Throws DivideByZeroException if b is 0
static double Divide(double a, double b);
};
}

  • สร้าง source file โดย Project -> Add New Item เลือก C++ file(.cpp) กำหนดชื่อ ให้เป็น MathFuncLib.cpp และ click Add จะได้ file เปล่ามาอีกหนึ่ง file
  • พื่อ implement function ต่าง ที่อยู่ใน header file ขั้นแรกเรา จะทำการ include header file ชื่อ MathFuncLib.h และจึง implement ตัง functions ทั้งหมด ตาม code นี้

// MathFuncsLib.cpp
// compile with: /c /EHsc
// post-build command: lib MathFuncsLib.obj

#include "MathFuncsLib.h"

#include <stdexcept>

using namespace std;

namespace MathFuncs
{
double MyMathFuncs::Add(double a, double b)
{
return a + b;
}

double MyMathFuncs::Subtract(double a, double b)
{
return a – b;
}

double MyMathFuncs::Multiply(double a, double b)
{
return a * b;
}

double MyMathFuncs::Divide(double a, double b)
{
if (b == 0)
{
throw new invalid_argument("b cannot be zero!");
}

return a / b;
}
}

เอาละครับ มาถึงตรงนี้เรา ก็ ได้ทำการ สร้าง Static library Project ขึ้นมาแล้ว ซึ่ง ถ้าเราสังเกตจะเห็น ว่ามันมี สอง file ชื่อ MathFuncsLib.h และ MathFuncsLib.cpp เราประกาศ class ไว้ใน MatchFuncsLib.h และ implement ใน MatchFuncsLib.cpp นะครับ หลังจากนี้ เราจะทำการ build project ให้เป็น library
ก่อนทำการ Build ให้ทำการ กำหนด project properties ให้ กำหนด configuration properties เป็น general และ Configuration Type ให้เป็น static library(.lib) กดปุ่ม OK เพื่อบันทึกการเปลี่ยนแปลง
Compile static library โดยการ Build Solution จาก Build menu เป็นการจบขั้นตอนการสร้าง static library เพื่อให้โปรแกรม อื่นเรียกใช้งาน

ต่อไปเป็นตัวอย่าง การเรียก ใช้  function จาก static library จาก console application

–          สร้าง console application ( win32 console application )  โดยอาจ ตั้งชื่อให้เป็น  MyExecRefsLib.cpp

–          เพื่อที่จะใช้ math routines ที่สร้างไว้ใน  static library เราจะต้องทำการ อ้างอิงไปหา หรือ Reference

  • ให้เลือก  Reference จาก Project menu ใน properties page dialog box ให้ขยาย common properties node และเลือก reference  แล้วเลือก Add New Reference
  • ขณะนี้ Add reference dialog box จะแสดง   ใน dialog box นี้แสดง รายการ  libraries ทั้งหมด ที่เราสามารถอ้างอิง  ในส่วนของ Project tab นั้นแสดง Projects ทั้งหมด ที่อยู่ใน solution และ libraries ต่าง ๆ ที่มีอยู่  ใน Project tab นี้เรา เลือก MathFuncsLib แล้ว OK
  • เพื่อให้เราสามารถ อ้างอิงไปยัง header files ของ static library ได้ เราต้อง แก้ไข include directories path  โดย ทำดังนี้ ใน Property Pages dialog box, ขยาย Configuration node ขยาย C/C++ node เลือก General
    • เพิ่ม  Additional Include Directories  พิมพ์ หรือ เลือก path ที่  MathFncsLib.h ตั้งอยู่

#include "stdafx.h"
#include <iostream>
#include "MathFuncsLib.h"

using namespace std;
//#include "iostream.h"

int _tmain(int argc, _TCHAR* argv[])
{
double a = 7.4;
int b = 99;

cout << "a + b = " <<
MathFuncs::MyMathFuncs::Add(a, b) << endl;
cout << "a – b = " <<
MathFuncs::MyMathFuncs::Subtract(a, b) << endl;
cout << "a * b = " <<
MathFuncs::MyMathFuncs::Multiply(a, b) << endl;
cout << "a / b = " <<
MathFuncs::MyMathFuncs::Divide(a, b) << endl;

return 0;
}

ทั้งหมด คงพอจะเห็น การทำงานนะครับ หวังว่าจะได้ อะไร ไปบ้าง นะครับ มีอะไร ไม่เข้าใจ mail มาคุยกันนะครับ s_teerapong2000@yahoo.com

Categories: C++, DOT NET, OOP