<?php
/**
* Sorting Head Builder For Dynamic Table
*
* @author Num
* @link http://web-programming-bookmark.blogspot.com
*/
class SortHead
{
protected $ccol = '';
protected $cdir = '';
protected $ncol = '';
protected $ndir = '';
protected $dfcol = '';
protected $dfdir = '';
protected $cols = array();
public $col = '';
public $dir = '';
public function __construct(array $cols,$cname,$dname,$dfcol,$dfdir){
$this->cols = $cols;
$this->dfcol = $dfcol;
$this->dfdir = $dfdir;
$this->ncol = $cname;
$this->ndir = $dname;
$this->ccol = $this->current_col();
$this->cdir = $this->current_dir($this->ccol,$dfdir);
$this->col = $this->ccol;
$this->dir = $this->cdir;
}
public function current_col(){
$ccol = empty($_GET[$this->ncol]) ? $this->dfcol : $_GET[$this->ncol];
$k = array_search($ccol,$this->cols,true);
return ($k === false) ?$this->dfcol :$this->cols[$k];
}
public function current_dir($col,$dfdir='desc'){
$odir = $dfdir == 'desc' ?'asc' :'desc';
if ($this->ccol == $col){
$dir = empty($_GET[$this->ndir]) ?$dfdir :$_GET[$this->ndir];
if ($dir != 'asc' && $dir != 'desc') $dir = 'desc';
$dir = $dir == 'desc' ?'desc' :'asc';
}else{
$dir = $dfdir;
}
return $dir;
}
public function next_dir($col,$dfdir='desc'){
$odir = $dfdir == 'desc' ?'asc' :'desc';
if ($this->ccol == $col){
$dir = empty($_GET[$this->ndir]) ?$dfdir :$_GET[$this->ndir];
if ($dir != 'asc' && $dir != 'desc') $dir = 'desc';
$dir = $dir == 'desc' ?'asc' :'desc';
}else{
$dir = $dfdir;
}
return $dir;
}
public function link($col,$dfdir='desc'){
$dir = $this->next_dir($col,$dfdir);
return '?'.$this->ncol.'='.$col.'&'.$this->ndir.'='.$dir;
}
public function dir($col,$dfdir='desc'){
return $this->current_dir($col,$dfdir);
}
}
$sh = new SortHead(array('name','title','id'),'st','sd','id','desc');
//safe for query
echo ', field=',$sh->col;
echo ', dir=',$sh->dir;
echo ', select * from tb order by '.$sh->col.' '.$sh->dir;
?>
<table border="1" cellpadding="10" cellspacing="0">
<tr>
<td>
<a href="<?php echo $sh->link('name','asc') ?>">name <?php echo $sh->dir('name','asc') ?></a>
</td>
<td>
<a href="<?php echo $sh->link('title') ?>">title <?php echo $sh->dir('title') ?></a>
</td>
<td>
<a href="<?php echo $sh->link('id') ?>">id <?php echo $sh->dir('id') ?></a>
</td>
</tr>
</table>
แสดงบทความที่มีป้ายกำกับ oop แสดงบทความทั้งหมด
แสดงบทความที่มีป้ายกำกับ oop แสดงบทความทั้งหมด
วันพฤหัสบดีที่ 5 พฤษภาคม พ.ศ. 2554
php sorting head builder class by num
วันจันทร์ที่ 11 เมษายน พ.ศ. 2554
try catch
ภาษาโปรแกรมซึ่งค่อนข้างใหม่มักจะมีคำสั่ง try catch
คำสั่งนี้ถ้าจะเรียกตรงๆ อาจจะเรียกว่าคำสั่ง
try ... catch ....
ลองทำ .... ถ้าเจอปัญหาก็ให้ทำ .... ด้วย
คุณสมบัติของคำสั่งนี้ก็เพื่อดัก error แปลกๆ ที่ดักโดยใช้คำสั่งตรวจสอบเงื่อนไขธรรมดาได้ยากหรือไม่ได้เลย
และหาวิธีจัดการกับ error ครับเช่น
ในการเขียน embedded database query ในภาษาโปรแกรม
มักจะเกิด database integrity error อย่างเช่น key ซ้ำ, รูปแบบข้อมูลไม่ตรงกับ field ซึ่งคำสั่ง try catch ก็จะช่วยให้เราจัดการกับ error นี้ได้เหมาะสมเช่นถ้า key ซ้ำเราอาจจะสร้าง key ขึ้นมาใหม่และทำการ insert อีกครั้ง
parse error ข้อมูลรูปแบบไม่ถูกต้องเช่น string แปลงเป็นวันที่ไม่ได้ ฯลฯ
ถ้าเกิด error เราก็อาจจะกำหนดค่าของตัวแปรเป็นค่า default ที่เราตั้งขึ้น
divide by zero การคำนวณที่เกิดข้อผิดพลาดจะทำอย่างไรเมื่อเกิดปัญหานี้ บางครั้งตัวเลขที่มาคำนวณอาจมีหลายตัว
การดักการหารด้วยศูนย์จะต้องเขียนซับซ้อน การใช้ try catch จะทำได้สะดวกกว่า
try
{
$i = 1/$x;
}catch(Exception $e){
//จัดการกับ error ด้วยวิธีต่างๆ
$i = -1;
}
คำสั่งนี้ถ้าจะเรียกตรงๆ อาจจะเรียกว่าคำสั่ง
try ... catch ....
ลองทำ .... ถ้าเจอปัญหาก็ให้ทำ .... ด้วย
คุณสมบัติของคำสั่งนี้ก็เพื่อดัก error แปลกๆ ที่ดักโดยใช้คำสั่งตรวจสอบเงื่อนไขธรรมดาได้ยากหรือไม่ได้เลย
และหาวิธีจัดการกับ error ครับเช่น
ในการเขียน embedded database query ในภาษาโปรแกรม
มักจะเกิด database integrity error อย่างเช่น key ซ้ำ, รูปแบบข้อมูลไม่ตรงกับ field ซึ่งคำสั่ง try catch ก็จะช่วยให้เราจัดการกับ error นี้ได้เหมาะสมเช่นถ้า key ซ้ำเราอาจจะสร้าง key ขึ้นมาใหม่และทำการ insert อีกครั้ง
parse error ข้อมูลรูปแบบไม่ถูกต้องเช่น string แปลงเป็นวันที่ไม่ได้ ฯลฯ
ถ้าเกิด error เราก็อาจจะกำหนดค่าของตัวแปรเป็นค่า default ที่เราตั้งขึ้น
divide by zero การคำนวณที่เกิดข้อผิดพลาดจะทำอย่างไรเมื่อเกิดปัญหานี้ บางครั้งตัวเลขที่มาคำนวณอาจมีหลายตัว
การดักการหารด้วยศูนย์จะต้องเขียนซับซ้อน การใช้ try catch จะทำได้สะดวกกว่า
try
{
$i = 1/$x;
}catch(Exception $e){
//จัดการกับ error ด้วยวิธีต่างๆ
$i = -1;
}
วันพฤหัสบดีที่ 10 มีนาคม พ.ศ. 2554
adodb class tutorial
adodb class เป็นคลาสที่ช่วยให้เขียนโปรแกรมติดต่อ database ง่ายขึ้นครับ
นอกจากนี้แล้วยังช่วย escape input เพื่อป้องกัน sql injection
และเป็น database layer ถ้าเขียนติดต่อ database ใน adodb ก็จะช่วยให้ย้ายไป database อื่นได้ง่ายขึ้นแก้ไขน้อยกว่าครับ
เริ่มต้นให้ download adodb มีหลายเวอร์ชั่นครับเลือกเอาเวอร์ชั่นที่ต้องการมาไฟล์ไฟล์เดียวครับ
http://sourceforge.net/projects/adodb/files/
นอกจากนี้แล้วยังช่วย escape input เพื่อป้องกัน sql injection
และเป็น database layer ถ้าเขียนติดต่อ database ใน adodb ก็จะช่วยให้ย้ายไป database อื่นได้ง่ายขึ้นแก้ไขน้อยกว่าครับ
เริ่มต้นให้ download adodb มีหลายเวอร์ชั่นครับเลือกเอาเวอร์ชั่นที่ต้องการมาไฟล์ไฟล์เดียวครับ
http://sourceforge.net/projects/adodb/files/
<?php
//config
$conf = array();
$conf['username'] = 'root';
$conf['password'] = '12345';
$conf['host'] = 'localhost';
$conf['db'] = 'test';
$conf['driver'] = 'mysqli'; //เลือกเป็น mysqli เพื่อจะได้ใช้งาน transaction ใน adodb class ได้
//setup and connect to db
require 'adodb/adodb.inc.php'; //เปลี่ยน path ไปยังไฟล์ adodb.inc.php ให้ถูกต้อง
$ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
$db = ADONewConnection($conf['driver']);
$db->Connect($conf['host'], $conf['username'], $conf['password'], $conf['db']);
$rs = $db->Execute('SET NAMES UTF8');
//create test data
$row = $db->GetRow("SHOW TABLES LIKE 'guestbook_test2'");
if (empty($row)){
$db->Execute(
'CREATE TABLE guestbook_test2 ('.
'id int(10) unsigned NOT NULL AUTO_INCREMENT,'.
'name varchar(20) NOT NULL,'.
'detail text NOT NULL,'.
'PRIMARY KEY (id)'.
')'
);
if ($db->ErrorNo()){
die($db->ErrorMsg());
}
echo 'create table successfully.<br>';
srand();
foreach(range(1,100) as $a){
$id = null; //insert id=null, update $id = id value
$name = md5(rand(1,10000));
$detail = sha1(rand(1,10000));
$db->Replace('guestbook_test2',compact('name','detail','id'),'id',true);
}
echo 'insert data successfully.<br>';
}
//pagination
$limit = 10;
$page = empty($_GET['page']) ? 1 : (int)$_GET['page'];
$rs = $db->PageExecute("SELECT * FROM guestbook_test2", $limit, $page);
$page_row_count = $rs->RowCount();
$row_count = $rs->MaxRecordCount();
$page_count = $rs->LastPageNo();
$rows = $rs->GetRows();
$rs->Close();
//test Execute and FetchRow
$select_id = 11;
$select_id2 = 15;
$rs = $db->Execute("SELECT * FROM guestbook_test2 WHERE id=? OR id=?", array($select_id,$select_id2));
$data = array();
while($row = $rs->FetchRow()){
$data[] = $row;
}
//test GetAssoc
$data2 = $db->GetAssoc("SELECT id,name FROM guestbook_test2 WHERE id BETWEEN ? AND ?",array(51,52));
//close connection
$db->Close();
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=tis-620" />
<title>adodb tutorial</title>
</head>
<body>
<!-- records of this page -->
<?php foreach($rows as $row):?>
<?php echo $row['id'],': ',$row['detail'],' ...... by <b>',$row['name'],'</b>';?>
<hr />
<?php endforeach;?>
<!-- page links -->
<?php for($i=1; $i<=$page_count; $i++):?>
<a href="?page=<?php echo $i;?>"><?php echo $i;?></a>
<?php endfor;?>
<hr />
<!-- records of select_id1, select_id2 -->
<?php foreach($data as $row):?>
<?php echo $row['id'],': ',$row['detail'],' ...... by <b>',$row['name'],'</b>';?>
<hr />
<?php endforeach;?>
<!-- records of 51 to 52 -->
<?php foreach($data2 as $id=>$value):?>
<?php echo $id,': ',$value,'</b>';?>
<hr />
<?php endforeach;?>
</body>
</html>
วันเสาร์ที่ 15 มกราคม พ.ศ. 2554
add dynamic method to object
<?php
class MyPrinter{
public static function echoStar($obj,$s){
echo '*******',$obj->data,'********',$s;
}
public static function echoLine($obj,$s){
echo '<br>--------------',$obj->data,'----------',$s;
}
public static function AddTo($obj){
$m = get_class_methods(__CLASS__);
foreach($m as $i)$obj->methods[$i] = array(__CLASS__,$i);
}
}
class MyData
{
public $methods = array();
public $data;
public function __construct($data) {
$this->data = $data;
}
public function __call($name,$args){
if (in_array($name,array_keys($this->methods))){
array_unshift($args,$this);
return call_user_func_array($this->methods[$name],$args);
}else{
die('call undefined function '.$name.'.');
}
}
}
$c = new MyData('hello');
MyPrinter::AddTo($c);
echo $c->echoStar('aaaaaaa');
echo '<hr>';
echo $c->echoLine('bbbbbbb');
?>
วันศุกร์ที่ 26 พฤศจิกายน พ.ศ. 2553
service user pattern
ในการเขียน function เราก็สามารถจัดระเบียบให้เป็น oop pattern รูปแบบหนึ่งได้เช่นกันครับ
( เป็น oop โดยการจัดรูปแบบ )
เช่น
/services/global-service.php
/services/project1-service.php
/services/project2-service.php
/services/project3-service.php
กับ
/project1/home.php
/project1/contactus.php
ซึ่งเราจะแบ่งส่วนให้มีส่วนประกาศ function เฉพาะใน mylibs เท่านั้น
และส่วนเรียกใช้งาน function ต่างๆจะมีอยู่ใน user เท่านั้น
ซึ่งเราอาจจะเรียกว่า oop แบบนี้ว่าเป็น service - user pattern ครับ
โดยมีไฟล์กลุ่มหนึ่งทำหน้าที่เป็น service ไฟล์อีกกลุ่มหนึ่งทำหน้าที่เรียกใช้ service object เรียกว่า user object
โดย service จะไม่เข้าไปปะปนกับ user ,แบ่งหน้าที่โดย service เป็นผู้ให้บริการส่วน user เป็นผู้เรียกและขอรับบริการจาก service เท่านั้น
global-service.php
<?php
function f1(){...}
?>
project1-service.php
<?php
function f2(){...}
?>
เราอาจจะนับได้ว่า project1-service.php ได้รับการสืบทอบมาจาก global-service.php
ซึ่งในการเรียก service มาใช้งานเพื่อความสะดวกเราจะสร้างไฟล์สำหรับ initial ค่าต่างๆ สำหรับ project
ซึ่่งไฟล์ดังกล่าวจะเป็น user ที่ทำการติดต่อกับ service ก่อน user อื่นๆ ทุกครั้งขึ้นมาโดยให้ชื่อว่า coordinator.php (แปลว่าผู้ติดต่อประสานงาน)
coordinator.php
<?php
require '/services/global-service.php';
require '/services/project1-service.php';
//connect db
//initial project1 global value
?>
home.php
<?php
require 'coordinator.php';
f1();
f2();
?>
เรียกได้ว่า home.php สืบทอดคุณสมบัติจาก coordinator.php เพื่อทำการติดต่อกับ service ต่างๆ
แต่การเขียนแบบนี้ส่วนใหญ่เรามักจะไม่ระมัดระวังโดยมักจะแทรก function ในไฟล์ user
และแอบแทรกคำสั่งทำงานทันทีในส่วน service
ซึ่งทำให้หาส่วนประกาศ function ยาก และหาลำดับขั้นตอนการทำงานตั้งแต่เริ่มต้นยากครับ
( เป็น oop โดยการจัดรูปแบบ )
เช่น
/services/global-service.php
/services/project1-service.php
/services/project2-service.php
/services/project3-service.php
กับ
/project1/home.php
/project1/contactus.php
ซึ่งเราจะแบ่งส่วนให้มีส่วนประกาศ function เฉพาะใน mylibs เท่านั้น
และส่วนเรียกใช้งาน function ต่างๆจะมีอยู่ใน user เท่านั้น
ซึ่งเราอาจจะเรียกว่า oop แบบนี้ว่าเป็น service - user pattern ครับ
โดยมีไฟล์กลุ่มหนึ่งทำหน้าที่เป็น service ไฟล์อีกกลุ่มหนึ่งทำหน้าที่เรียกใช้ service object เรียกว่า user object
โดย service จะไม่เข้าไปปะปนกับ user ,แบ่งหน้าที่โดย service เป็นผู้ให้บริการส่วน user เป็นผู้เรียกและขอรับบริการจาก service เท่านั้น
global-service.php
<?php
function f1(){...}
?>
project1-service.php
<?php
function f2(){...}
?>
เราอาจจะนับได้ว่า project1-service.php ได้รับการสืบทอบมาจาก global-service.php
ซึ่งในการเรียก service มาใช้งานเพื่อความสะดวกเราจะสร้างไฟล์สำหรับ initial ค่าต่างๆ สำหรับ project
ซึ่่งไฟล์ดังกล่าวจะเป็น user ที่ทำการติดต่อกับ service ก่อน user อื่นๆ ทุกครั้งขึ้นมาโดยให้ชื่อว่า coordinator.php (แปลว่าผู้ติดต่อประสานงาน)
coordinator.php
<?php
require '/services/global-service.php';
require '/services/project1-service.php';
//connect db
//initial project1 global value
?>
home.php
<?php
require 'coordinator.php';
f1();
f2();
?>
เรียกได้ว่า home.php สืบทอดคุณสมบัติจาก coordinator.php เพื่อทำการติดต่อกับ service ต่างๆ
แต่การเขียนแบบนี้ส่วนใหญ่เรามักจะไม่ระมัดระวังโดยมักจะแทรก function ในไฟล์ user
และแอบแทรกคำสั่งทำงานทันทีในส่วน service
ซึ่งทำให้หาส่วนประกาศ function ยาก และหาลำดับขั้นตอนการทำงานตั้งแต่เริ่มต้นยากครับ
วันพฤหัสบดีที่ 25 พฤศจิกายน พ.ศ. 2553
singleton pattern
<?php
//singleton โครงสร้างเพื่อป้องกันไม่ให้มีการสร้าง instance ขึ้นหลายครั้ง
//อย่างเช่น database class connection ที่ควรจะมีแค่ครั้งเดียว
class Example
{
private static $obj;
private function __construct() {}
public static function singleton() // singleton method
{
if (!isset(self::$obj)) {
$c = __CLASS__;
self::$obj = new $c;
}
return self::$obj;
}
public function __clone(){ // Prevent users to clone the instance
trigger_error('Clone is not allowed.', E_USER_ERROR);
}
}
$obj = Example::singleton();
//$obj = new Example();// error (private constructor)
//$obj2 = clone $obj;// error
?>
template method
<?php
//template method คือ
//method ที่เตรียมไว้เป็นโครงสร้างแต่คำสั่งที่จะปรับปรุงเงื่อนไขต่างๆ ที่ซับซ้อน
//จะมอบให้เป็นหน้าที่ของ function ที่มันเรียกอีกทีหนึ่ง
abstract class Shape
{
public abstract function height();
public abstract function width();
public function area(){ //template method
return $this->height()*$this->width();
}
}
class Rectangle extends Shape
{
function height(){ return 10;}
function width(){ return 20;}
}
class Square extends Shape
{
function height(){ return 10;}
function width(){ return 10;}
}
$r = new Rectangle(); echo $r->area();
echo '<br','>';
$s = new Square(); echo $s->area();
?>
วันอาทิตย์ที่ 21 พฤศจิกายน พ.ศ. 2553
Abstract factory pattern
abstract class เพื่อให้เรียกใช้งาน class ต่างๆ ที่มีรายชื่อคำสั่ง function เหมือนกันได้
โดยสามารถสลับไปใช้ class ที่มีรายชื่อคำสั่งเหมือนกันได้ผ่าน configuration
โดยสามารถสลับไปใช้ class ที่มีรายชื่อคำสั่งเหมือนกันได้ผ่าน configuration
<?php
abstract class DbFactory
{
public static function getFactory($type){
$type .= 'Db';
return new $type;
}
public abstract function connect();
//public abstract function query();
//public abstract function fetch();
//public abstract function close();
}
class MysqlDb extends DbFactory
{
public function connect(){
echo '[Mysql Connect]';
}
}
class MssqlDb extends DbFactory
{
public function connect(){
echo '[Mssql Connect]';
}
}
$config = 'Mssql';
$db = DbFactory::getFactory($config);
$db->connect();
//...
?>
oop vs procedural
พูดถึงตัวแปร global ก่อนนะครับ
ถ้าใช้ตัวแปร global แสดงว่าทุก function สามารถเข้าถึงตัวแปร global ได้หมด
เวลาเกิด error ขึ้นในโปรแกรม โดยเฉพาะ error จาก logic เราจะไม่สามารถวิเคราะห์ได้ว่า
ภายใน function ไหนที่เป็นตัวเข้าไปแก้ไขทำใ้ห้ตัวแปร global หรือโปรแกรมของเราทำงานผิดพลาด
ดังนั้นภายในโปรแกรมควรจะจำกัดตัวแปร global ให้น้อยที่สุดครับ
ถึงจะมีข้อแนะนำว่าไม่ควรใช้ตัวแปร global หรือใช้น้อยที่สุด
แต่ว่าบางโปรแกรมที่จำเป็นต้องใช้ตัวแปรร่วมระหว่าง function ครับ
f1($a); ต้องการตัวแปร $x ไปประมวลผล
f2($a,$b); ต้องการตัวแปร $x ไปประมวลผล
f2($c,$d); ต้องการตัวแปร $x ไปประมวลผล
ซึ่งถ้าเราไม่ใช้ตัวแปร global เราก็ต้องเขียนแบบนี้ครับ
f1($a,$x);
f2($a,$b,$x);
f2($c,$d,$x);
แต่ว่าปัญหาเกิดครับ มีโค้ดซ้ำ ได้แก่ ,$x ซึ่งทำให้โปรแกรมเขียนและอ่านยากขึ้น
และเราไม่สามารถบอกได้ว่า function f1,f2,f3 มีความสัมพันธ์ต่อเนื่องกันหรือไม่
ซึ่งถ้าเขียนแบบ function ก็ทำได้ครับเช่น mysql_connect, mysql_query อันนี้เป็นการระบุความสัมพันธ์จาก คำหน้าหน้าชื่อ function
สำหรับการเขียนแบบ class นะครับ
เป็นการแก้โจทย์ปัญหาเกี่ยวกับตัวแปรร่วมที่ใช้ function ต่างๆ โดยไม่ต้องส่ง parameter ไปมา
การสร้างเป็น class นั้น ตัวแปรที่เป็นตัวแปรร่วมจะเรียกว่า member ของ class
ส่วน function จะเรียกว่าเป็น behavior ของ class (บรรทัดนี้และบรรทัดบนนี้ไม่รู้ถูกป่าวนะผมก็แค่ฟังๆผ่านๆจาก lecture ที่มหาวิทยาลัย)
ประโยชน์อย่างหนึ่งก็คือจัดกลุ่มของ function ที่สัมพันธ์กันโดยไม่ต้องมี คำนำหน้าเดียวกัน และไว้ที่เดียวกัน
ประโยชน์อีกอย่างคือ ตัวแปร member จะถูกแก้ไขโดย function และำคำสั่งภายใน class เท่านั้น
ทำให้พอจะระบุตำแหน่งของ error ได้ง่ายขึ้น เพราะในการเขียนแบบ oop ไม่จำเป็นต้องใช้ตัวแปร global มากมายอย่างในแบบ procedural
ประโยชน์อีกอย่างก็คือการจัดรูปแบบของ program จะมีลักษณะเป็นวัตถุกลุ่มก้อนมากขึ้น
การมองการเขียนโปรแกรมในรูปแบบ oop จะต่างจาก function ซึ่งเพียงแต่จะเน้นเพียงวัตถุประสงค์ให้โปรแกรมทำงานสำเร็จลุล่วง
แต่จะมองว่าเป็นการพยายามทำให้วัตถุต่างๆ ในโปรแกรมสื่อสารโต้ตอบกัน ใน oop เรียกว่าส่ง message การทำแบบนี้จะทำให้โปรแกรมที่ค่อนข้างซับซ้อน สามารถนำกลับมาใช้ใหม่ได้สะดวกครับ แต่การเขียนแบบอิสระก็จะทำให้เกิดความไม่เป็นระเบียบได้เหมือนกันจึงมีผู้ออกแบบ oop design pattern ที่ใช้กันบ่อยๆ ขึ้นมาครับ ซึ่งแม้ว่าการเขียนแบบ function ธรรมดาก็ทำได้เช่นเหมือนกัน แต่จะจัดระเบียบยุ่งยากกว่าแบบ class อยู่พอควรครับ
ถ้าใช้ตัวแปร global แสดงว่าทุก function สามารถเข้าถึงตัวแปร global ได้หมด
เวลาเกิด error ขึ้นในโปรแกรม โดยเฉพาะ error จาก logic เราจะไม่สามารถวิเคราะห์ได้ว่า
ภายใน function ไหนที่เป็นตัวเข้าไปแก้ไขทำใ้ห้ตัวแปร global หรือโปรแกรมของเราทำงานผิดพลาด
ดังนั้นภายในโปรแกรมควรจะจำกัดตัวแปร global ให้น้อยที่สุดครับ
ถึงจะมีข้อแนะนำว่าไม่ควรใช้ตัวแปร global หรือใช้น้อยที่สุด
แต่ว่าบางโปรแกรมที่จำเป็นต้องใช้ตัวแปรร่วมระหว่าง function ครับ
f1($a); ต้องการตัวแปร $x ไปประมวลผล
f2($a,$b); ต้องการตัวแปร $x ไปประมวลผล
f2($c,$d); ต้องการตัวแปร $x ไปประมวลผล
ซึ่งถ้าเราไม่ใช้ตัวแปร global เราก็ต้องเขียนแบบนี้ครับ
f1($a,$x);
f2($a,$b,$x);
f2($c,$d,$x);
แต่ว่าปัญหาเกิดครับ มีโค้ดซ้ำ ได้แก่ ,$x ซึ่งทำให้โปรแกรมเขียนและอ่านยากขึ้น
และเราไม่สามารถบอกได้ว่า function f1,f2,f3 มีความสัมพันธ์ต่อเนื่องกันหรือไม่
ซึ่งถ้าเขียนแบบ function ก็ทำได้ครับเช่น mysql_connect, mysql_query อันนี้เป็นการระบุความสัมพันธ์จาก คำหน้าหน้าชื่อ function
สำหรับการเขียนแบบ class นะครับ
เป็นการแก้โจทย์ปัญหาเกี่ยวกับตัวแปรร่วมที่ใช้ function ต่างๆ โดยไม่ต้องส่ง parameter ไปมา
การสร้างเป็น class นั้น ตัวแปรที่เป็นตัวแปรร่วมจะเรียกว่า member ของ class
ส่วน function จะเรียกว่าเป็น behavior ของ class (บรรทัดนี้และบรรทัดบนนี้ไม่รู้ถูกป่าวนะผมก็แค่ฟังๆผ่านๆจาก lecture ที่มหาวิทยาลัย)
ประโยชน์อย่างหนึ่งก็คือจัดกลุ่มของ function ที่สัมพันธ์กันโดยไม่ต้องมี คำนำหน้าเดียวกัน และไว้ที่เดียวกัน
ประโยชน์อีกอย่างคือ ตัวแปร member จะถูกแก้ไขโดย function และำคำสั่งภายใน class เท่านั้น
ทำให้พอจะระบุตำแหน่งของ error ได้ง่ายขึ้น เพราะในการเขียนแบบ oop ไม่จำเป็นต้องใช้ตัวแปร global มากมายอย่างในแบบ procedural
ประโยชน์อีกอย่างก็คือการจัดรูปแบบของ program จะมีลักษณะเป็นวัตถุกลุ่มก้อนมากขึ้น
การมองการเขียนโปรแกรมในรูปแบบ oop จะต่างจาก function ซึ่งเพียงแต่จะเน้นเพียงวัตถุประสงค์ให้โปรแกรมทำงานสำเร็จลุล่วง
แต่จะมองว่าเป็นการพยายามทำให้วัตถุต่างๆ ในโปรแกรมสื่อสารโต้ตอบกัน ใน oop เรียกว่าส่ง message การทำแบบนี้จะทำให้โปรแกรมที่ค่อนข้างซับซ้อน สามารถนำกลับมาใช้ใหม่ได้สะดวกครับ แต่การเขียนแบบอิสระก็จะทำให้เกิดความไม่เป็นระเบียบได้เหมือนกันจึงมีผู้ออกแบบ oop design pattern ที่ใช้กันบ่อยๆ ขึ้นมาครับ ซึ่งแม้ว่าการเขียนแบบ function ธรรมดาก็ทำได้เช่นเหมือนกัน แต่จะจัดระเบียบยุ่งยากกว่าแบบ class อยู่พอควรครับ
วันอังคารที่ 16 พฤศจิกายน พ.ศ. 2553
non new and method chaining
แทนที่จะ new object ของ class จากด้านนอก เราสามารถเขียนอีกแบบซึ่งจะสะดวกกว่าในหลายๆ กรณี
โดยการประกาศตัวแปร object ภายใน static method ของ class นั้นแทน
และใช้วิธี method chaining เพื่อลดการเรียกชื่อผ่านตัวแปร object จากด้านนอกและ
เรียกๆ method ที่คืนค่าของ class ไปเรื่อยๆ เพื่อประมวลผล (method chaining)
และสิ้นสุดด้วยการเรียก result method เพื่อนำค่าที่ได้ไปใช้งาน ดังนี้ครับ
PHP
C#
โดยการประกาศตัวแปร object ภายใน static method ของ class นั้นแทน
และใช้วิธี method chaining เพื่อลดการเรียกชื่อผ่านตัวแปร object จากด้านนอกและ
เรียกๆ method ที่คืนค่าของ class ไปเรื่อยๆ เพื่อประมวลผล (method chaining)
และสิ้นสุดด้วยการเรียก result method เพื่อนำค่าที่ได้ไปใช้งาน ดังนี้ครับ
PHP
<?php
class Cal
{
protected $n;
public function __construct($n){
$this->n = $n;
}
public function add($n){
$this->n += $n;
return $this;
}
public function mul($n){
$this->n *= $n;
return $this;
}
public function result(){
return $this->n;
}
public static function create($n){
return new Cal($n);
}
}
echo Cal::create(3)->add(5)->mul(100)->result();
?>
C#
// Cal.cs
public class Cal
{
protected int n;
public static Cal Create(int n)
{
return new Cal(n);
}
public Cal(int n)
{
this.n = n;
}
public Cal Add(int n){
this.n += n;
return this;
}
public Cal Mul(int n)
{
this.n *= n;
return this;
}
public int Result()
{
return this.n;
}
}
//Example
//Response.Write(Cal.Create(10).Add(1).Mul(3).Result());
วันอาทิตย์ที่ 7 พฤศจิกายน พ.ศ. 2553
OOP Basics
OOP (Object-Oriented Programing)
การโปรแกรมเชิงวัตถุ มีคำอธิบายว่าคือการเขียนโปรแกรมให้มีคุณสมบัติเหมือนวัตถุ
ซึ่งเราสามารถจำแนกรูปแบบของการเขียนโปรแกรมเชิงวัตถุพื้นได้แก่
1) Encapsulation คุณสมบัติการห่อหุ้ม
ถ้าการเขียน class ใดๆ ที่มีการใช้ตัวแปร global หรือการเข้าแก้ไขตัวแปร member ของ object อื่นๆ ตัวแปรอื่นๆ นอกจากตัวแปร member ของมันเอง
ถือว่าเป็นการเขียนรูปแบบ OOP ที่ไม่ดี อาจจะเรียกได้ว่าไม่ใช่ OOP เลยก็ได้
คุณสมบัตินี้ถือเป็นคุณสมบัติที่สำคัญมาก เพราะถ้า object หนึ่งมีการ share ตัวแปร global กับ object อื่นๆ แล้วย่อมทำให้คุณสมบัติการจัดกลุ่มตัวแปร scope ของตัวแปร
สูญเสียไปอย่างสิ้นเชิง ถือว่าไม่ต่างจากการเขียนโปรแกรมแบบ Procedural ทั่วๆ ไป นอกจากนี้ยังอ่านและแก้ไขโปรแกรมได้ยากกว่าอีกด้วย
2) Inheritance คุณสมบัติการสืบทอด
โดยปกติแล้วเราไม่จำเป็นต้องสร้าง class และสร้าง class ที่สืบทอดจากอีก class นี้เพื่อให้ class มีคุณสมบัติเป็น OOP
แต่การสืบทอดหรือใน php ที่เราใช้ keyword extends นั้นมีประโยชน์ที่ทำให้เราสามารถใช้งานโค้ดได้อย่างมีระเบียบมากขึ้น
ตัวอย่างเช่น class A มีคุณสมบัติ Encapsulation ดีมาก สามารถนำไปใช้ได้หลายงาน
แต่ในการใช้งานจริงเราอาจจำเป็นต้องมี class ซึ่งใช้งานเฉพาะด้าน ถ้าหากเราเข้าไปแก้ไข class A จะทำให้ class A สูญเสียคุณสมบัติในการนำกลับมาใช้ใหม่ในทันที
เพื่อที่จะสามารถนำ class มาใช้งานเฉพาะด้านเราจึงสร้าง class B extends A เพื่อให้ class B ของเรามีคุณสมบัติเหมือน class A
และ class B จะถูกนำไปแก้ไขเพื่อใช้กับงานประยุกต์เฉพาะด้านโดยไม่ส่งผลกระทบกับ class ต้นฉบับ
3) Polymorphism การเรียกใช้แบบเดียวแต่ทำงานตอบสนองได้หลายแบบ
ตัวอย่างนี้ของคุณสมบัตินี้มักจะถูกยกตัวอย่างในรูปของ class Shape
โดยมี class Circle, Rectangle, Triangle เป็นคลาสลูก หรือ class ที่สืบทอดมาจาก class Shape
ซึ่งชี้ให้เห็นว่าการเรียกใช้งาน function cal นั้น ซึ่งมีการเรียก method cal ของคลาสลูกของ Shape สามารถทำงานปรับเปลี่ยนไปตามคุณสมบัติของคลาสลูก ได้
ประโยชน์ของการมี class ที่เป็น polymorphism ได้แก่ class หรือ function อื่นๆ ที่เรียก method cal นั้น
นั่นก็คือไม่เพียงแต่ class หรือ function จะยึดติดให้การทำงานใช้ได้เฉพาะกับ class Shape เท่านั้น แต่
class หรือ function เดิมนั้นยังสามารถนำกลับมาใช้ใหม่กับคลาสลูกของ Shape ได้ โดยมีการทำงานตามคุณสมบัติของ class ลูกโดยเฉพาะอีกด้วย
กล่าวคือเราสร้าง function เดียวแต่สามารถเปลี่ยนแปลงการทำงานโดยไม่ต้องแก้ที่ function หลัก แต่เพียงแต่แก้ที่ method ของคลาสลูกเท่านั้น
การเขียนในรูปแบบ OOP นี้จึงทำให้โค้ดหลักมีความเป็นระเบียบ โค้ดรูปแบบเดียวสามารถ
เปลี่ยนแปลงการทำงานโดยเพียงแค่เปลี่ยนชื่อคลาสลูกหรือเปลี่ยนตัวแปรเป็น object ของคลาสลูก
การโปรแกรมเชิงวัตถุ มีคำอธิบายว่าคือการเขียนโปรแกรมให้มีคุณสมบัติเหมือนวัตถุ
ซึ่งเราสามารถจำแนกรูปแบบของการเขียนโปรแกรมเชิงวัตถุพื้นได้แก่
1) Encapsulation คุณสมบัติการห่อหุ้ม
ถ้าการเขียน class ใดๆ ที่มีการใช้ตัวแปร global หรือการเข้าแก้ไขตัวแปร member ของ object อื่นๆ ตัวแปรอื่นๆ นอกจากตัวแปร member ของมันเอง
ถือว่าเป็นการเขียนรูปแบบ OOP ที่ไม่ดี อาจจะเรียกได้ว่าไม่ใช่ OOP เลยก็ได้
คุณสมบัตินี้ถือเป็นคุณสมบัติที่สำคัญมาก เพราะถ้า object หนึ่งมีการ share ตัวแปร global กับ object อื่นๆ แล้วย่อมทำให้คุณสมบัติการจัดกลุ่มตัวแปร scope ของตัวแปร
สูญเสียไปอย่างสิ้นเชิง ถือว่าไม่ต่างจากการเขียนโปรแกรมแบบ Procedural ทั่วๆ ไป นอกจากนี้ยังอ่านและแก้ไขโปรแกรมได้ยากกว่าอีกด้วย
2) Inheritance คุณสมบัติการสืบทอด
โดยปกติแล้วเราไม่จำเป็นต้องสร้าง class และสร้าง class ที่สืบทอดจากอีก class นี้เพื่อให้ class มีคุณสมบัติเป็น OOP
แต่การสืบทอดหรือใน php ที่เราใช้ keyword extends นั้นมีประโยชน์ที่ทำให้เราสามารถใช้งานโค้ดได้อย่างมีระเบียบมากขึ้น
ตัวอย่างเช่น class A มีคุณสมบัติ Encapsulation ดีมาก สามารถนำไปใช้ได้หลายงาน
แต่ในการใช้งานจริงเราอาจจำเป็นต้องมี class ซึ่งใช้งานเฉพาะด้าน ถ้าหากเราเข้าไปแก้ไข class A จะทำให้ class A สูญเสียคุณสมบัติในการนำกลับมาใช้ใหม่ในทันที
เพื่อที่จะสามารถนำ class มาใช้งานเฉพาะด้านเราจึงสร้าง class B extends A เพื่อให้ class B ของเรามีคุณสมบัติเหมือน class A
และ class B จะถูกนำไปแก้ไขเพื่อใช้กับงานประยุกต์เฉพาะด้านโดยไม่ส่งผลกระทบกับ class ต้นฉบับ
3) Polymorphism การเรียกใช้แบบเดียวแต่ทำงานตอบสนองได้หลายแบบ
ตัวอย่างนี้ของคุณสมบัตินี้มักจะถูกยกตัวอย่างในรูปของ class Shape
โดยมี class Circle, Rectangle, Triangle เป็นคลาสลูก หรือ class ที่สืบทอดมาจาก class Shape
ซึ่งชี้ให้เห็นว่าการเรียกใช้งาน function cal นั้น ซึ่งมีการเรียก method cal ของคลาสลูกของ Shape สามารถทำงานปรับเปลี่ยนไปตามคุณสมบัติของคลาสลูก ได้
ประโยชน์ของการมี class ที่เป็น polymorphism ได้แก่ class หรือ function อื่นๆ ที่เรียก method cal นั้น
นั่นก็คือไม่เพียงแต่ class หรือ function จะยึดติดให้การทำงานใช้ได้เฉพาะกับ class Shape เท่านั้น แต่
class หรือ function เดิมนั้นยังสามารถนำกลับมาใช้ใหม่กับคลาสลูกของ Shape ได้ โดยมีการทำงานตามคุณสมบัติของ class ลูกโดยเฉพาะอีกด้วย
กล่าวคือเราสร้าง function เดียวแต่สามารถเปลี่ยนแปลงการทำงานโดยไม่ต้องแก้ที่ function หลัก แต่เพียงแต่แก้ที่ method ของคลาสลูกเท่านั้น
การเขียนในรูปแบบ OOP นี้จึงทำให้โค้ดหลักมีความเป็นระเบียบ โค้ดรูปแบบเดียวสามารถ
เปลี่ยนแปลงการทำงานโดยเพียงแค่เปลี่ยนชื่อคลาสลูกหรือเปลี่ยนตัวแปรเป็น object ของคลาสลูก
<?php
class Shape{
var $name='S';
function cal(){
return 0;
}
}
class Circle extends Shape{
var $ray;
var $name='C';
function Circle($ray){
$this->ray = $ray;
}
function cal(){
return (22/7)*pow($this->ray,2);
}
}
class Rectangle extends Shape{
var $edge;
var $name='R';
function Rectangle($edge){
$this->edge = $edge;
}
function cal(){
return pow($this->edge,2);
}
}
class Triangle extends Shape{
var $base;
var $height;
var $name='T';
function Triangle($base,$height){
$this->base = $base;
$this->height = $height;
}
function cal(){
return 0.5*$this->base*$this->height;
}
}
function cal(Shape $obj){
return $obj->cal();
}
$c = new Circle(10);
$r = new Rectangle(10);
$t = new Triangle(10,10);
foreach(array($c,$r,$t) as $shape){
echo '<br',' />',$shape->name,' = ',cal($shape);
}
?>
PHP OOP tutorial
object หรือ instance ของ class
$q1 และ $q2 เป็น object ของ class Quote
ซึ่ง class ก็เป็นเหมือนนามธรรม อย่างเช่น รถยนต์ซึ่งมีคุณสมบัติต่างๆ
ส่วน object ก็เหมือนรูปธรรมอย่างเช่น รถยนต์คันหนึ่งซึ่งมีคุณสมบัติรถยนต์
จะเห็นว่า object $q1 และ $q2 จะมีตัวแปร s เป็นคุณสมบัติเฉพาะตัว
เราอาจจะเปรียบเทียบ s เป็นลายของรถยนต์
ซึ่งเสมือนกับว่า $q1 เป็นรถยนต์คันหนึ่งมีลาย hello
ซึ่งเสมือนกับว่า $q2 เป็นรถยนต์คันหนึ่งมีลาย doraemon
และมี squote เป็นการทำงานของ class อาจจะเทียบเป็นการแสดงลายของรถยนต์
แม้ว่ามีการทำงานเหมือนกัน แต่การแสดงลายจะแสดงต่างกัน เพราะรถยนต์แต่ละคันมีลายต่างกันนั่นเอง
<?php
class Quote{
protected $s = '';
function __construct($s){
$this->s = $s;
}
function squote(){
return "'".$this->s."'";
}
}
$q1 = new Quote('hello');
$q2 = new Quote('doraemon');
echo $q1->squote();
echo ', ';
echo $q2->squote();
?>
จะใช้ private protected หรือ public
โดยปกติไม่ควรตั้งสมาชิกตัวแปรให้เป็น public เพราะจะทำให้สามารถทำให้ object อื่นๆ สามารถเข้ามาแก้ไขเปลี่ยนแปลงสมาชิกตัวแปรของ object ได้โดยตรง
อย่างไรก็ตามถ้าสมาชิกตัวแปรเป็นตัวแปรที่ใช้เป็น configuration หรือการกำหนด setting การทำงานของ object ให้กำหนดเป็น public เพื่อความสะดวก
ทำให้ object ต่างๆ สามารถเข้ามาแก้ไข configuration ได้ทันที ไม่จำเป็นที่เราจะต้องสร้าง function เพื่อ set ค่าจำนวนมาก
<?php
class Car{
protected $color = 'red';
private $wheal = 4;
public $tag = 'great car';
}
class Bmw extends Car{
protected $color = 'blue';
public $tag = 'bmw car';
}
?>
อย่างไรก็ตามการกำหนดค่าผ่านตัวแปร public จะมีข้อเสียก็คือการกำหนดชื่อตัวแปรผิดได้ เพราะ php จะไม่มีการแจ้งเตือนเมื่อมีการกำหนดค่าตัวแปรใหม่ซึ่งไม่มีใน class
เราสามารถทำให้เกิดการ warning เมื่อกำหนดค่าตัวแปรที่ไม่มีใน class ได้ โดยใช้ member overloading
<?php
class Car{
protected $color = 'red';
private $wheal = 4;
public $tag = 'great car';
public function __set($name, $val){
echo "<br/>Error: Setting undefined member $name to $val";
}
}
$c = new Car();
$c->taggg = 'normal car';
?>
member accessibility ที่แนะนำให้ใช้เป็นประจำได้แก่ protected เพื่อช่วยให้ class ที่สืบทอด class ที่เราสร้างขึ้นสามารถใช้ตัวแปรได้เช่นกัน
อย่างไรก็ตามในบางครั้งอาจจะมีตัวแปรพิเศษซึ่งเราใช้เป็นตัวแปรชั่วคราวสำหรับเทคนิคการโปรแกรมบางอย่าง
ซึ่งไม่ต้องการให้ class ที่สืบทอด class นี้รู้จักตัวแปรหรือนำตัวแปรไปใช้ เราจะตั้งตัวแปรนี้เป็น private
ตัวแปรที่เป็น protected และ private ก็สามารถถูกเปลี่ยนแปลงค่าจากภายนอกได้เช่นกันโดยผ่านทาง method ที่เป็น public
ข้อดีของการใช้ public method ในการเข้าถึงตัวแปร private หรือ protected ก็คือสามารถกรองข้อมูลที่จะถูกกำหนดค่าของสมาชิกตัวแปร
ในตัวอย่างข้างนี้เราใช้ function __set (member overloading function) และ setColor (public function)
เพื่อทำการกรองข้อมูลที่ถูกกำหนดค่าเข้ามาให้เป็นตัวอักษรตัวเล็กเสมอ
<?php
class Car{
protected $color = 'red';
private $wheal = 4;
public $tag = 'great car';
public function __set($name, $val){
if ($name == 'color'){
$this->color = strtolower($val);
}
}
public function setColor($c){
$this->color = strtolower($c);
}
public function getColor(){
return $this->color;
}
}
$c = new Car();
$c->color = 'GREEN';
echo ',',$c->getColor();
$c->setColor('BLUE');
echo ',',$c->getColor();
?>
ควรจะเขียนเป็น static หรือ non static method
เรามักจะละเลยการเขียน static method หรือในบางเวลาเราจะเรียกว่า class method
เพราะว่าในหนังสือสอนการเขียน OOP ส่วนใหญ่จะไม่ได้ให้ความสำคัญกับ static method มากเท่ากัีบ method ของ object หรือ method ที่ต้องประกาศ object ก่อนถึงจะเรียกใช้งานได้
แต่ในความเป็นจริงแล้วคำสั่งส่วนใหญ่ควรจะเขียนเป็น static ในขั้นเริ่มต้นเสมอ
นอกจากวิเคราะห์แล้วว่า static method ไม่สามารถตอบโจทย์ในการแก้ทำจุดประสงค์ของ method เนื่องจากข้อจำกัดของความเป็น static function ไม่สามารถทำได้ ได้แก่
- จำเป็นต้องใช้ตัวแปรของ object เพื่อเก็บสถานะของการเรียกคำสั่ง
- จำเป็นต้องเรียกใช้ method ของ object เพื่อให้การทำงานเสร็จสมบูรณ์
static method นั้นดีกว่า object method
- เพราะว่า สามารถเรียกใช้งานได้โดยไม่ต้องประกาศ object ก่อน
- static method สามารถทำงานได้เร็วกว่า
- ไม่มีผลกระทบกับ ตัวแปรอื่นๆ ทำให้ debug ได้ง่ายและมั่นใจในความถูกต้องได้ง่ายกว่า
เพราะว่าในหนังสือสอนการเขียน OOP ส่วนใหญ่จะไม่ได้ให้ความสำคัญกับ static method มากเท่ากัีบ method ของ object หรือ method ที่ต้องประกาศ object ก่อนถึงจะเรียกใช้งานได้
แต่ในความเป็นจริงแล้วคำสั่งส่วนใหญ่ควรจะเขียนเป็น static ในขั้นเริ่มต้นเสมอ
นอกจากวิเคราะห์แล้วว่า static method ไม่สามารถตอบโจทย์ในการแก้ทำจุดประสงค์ของ method เนื่องจากข้อจำกัดของความเป็น static function ไม่สามารถทำได้ ได้แก่
- จำเป็นต้องใช้ตัวแปรของ object เพื่อเก็บสถานะของการเรียกคำสั่ง
- จำเป็นต้องเรียกใช้ method ของ object เพื่อให้การทำงานเสร็จสมบูรณ์
static method นั้นดีกว่า object method
- เพราะว่า สามารถเรียกใช้งานได้โดยไม่ต้องประกาศ object ก่อน
- static method สามารถทำงานได้เร็วกว่า
- ไม่มีผลกระทบกับ ตัวแปรอื่นๆ ทำให้ debug ได้ง่ายและมั่นใจในความถูกต้องได้ง่ายกว่า
สมัครสมาชิก:
บทความ (Atom)