یک پیادهسازی از دادهساختار آرایهی ساده در پیاچپی (PHP)، نسخه ۳
در نسخههای قبلی مقالهی دادهساختار آرایهی یکبعدی در زبان برنامهنویسی پیاچپی، ما یک دادهساختار به نام AbstractOneDimArray
توسعه دادیم و از اینترفیس ArrayAccess
برای پیادهسازی عملگر [] در دادهساختار آرایهی خود استفاده کردیم.
در این نسخه، ما از مفهوم اینترفیس استفادهی بیشتری میکنیم و از یک ابزار جدید نیز استفاده میکنیم. در واقع ما از یک دیزاین پترن معروف به نام کارخانه (Factory) استفاده میکنیم. ما یک فکتوری را برای استفاده در اجرای تستی دادهساختار پیادهسازی میکنیم.
همچنین دو کلاس واقعی FloatOneDimArray
و StringOneDimArray
را از کلاس پایهی خود مشتق میکنیم.
گام اول این است که چگونه از مفهوم اینترفیس استفاده کنیم. ما یک اینترفیس میسازیم که دادهساختار آرایهی ما رو مجبور میکند که متد Size
را پیادهسازی کند.
interface OneDimArrayInterface
{
function __construct(int $size);
function Size(): int;
}
بنابراین در مورد ما، ما از اینترفیس به این صورت استفاده میکنیم.
abstract class AbstractOneDimArray implements OneDimArrayInterface, \ArrayAccess
توجه کردید که ما همزمان از دو اینترفیس استفاده کردیم. این یک کد معتبر پیاچپی (PHP) و یک ابزار بسیار کارآمد است.
بقیهی پیادهسازی کلاس پایه، به جز این تغییر کوچک، بدون تغییر باقی میماند
اکنون، ما قصد داریم آرایههای بیشتری را پیاده کنیم، برای نوع داده اعشاری و رشتهای.
class FloatOneDimArray extends AbstractOneDimArray
{
protected function getValueType() : string
{
return gettype(1.0);
}
protected function getDefaultValue()
{
return 0.0;
}
}
class StringOneDimArray extends AbstractOneDimArray
{
protected function getValueType() : string
{
return gettype('');
}
protected function getDefaultValue()
{
return '';
}
}
یک بار دیگر، مشاهده میکنید که ما تنها نیاز به پیادهسازی دو متد getValueType
و getDefaultValue
داریم. این قدرت برنامهنویسی شیگرا (OOP) است.
اکنون، همانطور که قول دادیم، ما قصد داریم از دیزاین پترن فکتوری برای تمیزتر کردن اجرای تستی استفاده کنیم. ما کلاس OneDimArrayFactory
را پیاده میکنیم:
class OneDimArrayFactory
{
public static function Make(string $type, int $size)
{
switch ($type)
{
case 'int':
case 'integer':
return new IntOneDimArray($size);
break;
case 'float':
case 'double':
return new FloatOneDimArray($size);
break;
case 'string':
default:
return new StringOneDimArray($size);
// throw new Exception('OneDimArray is not implemented for the type ' . $type);
break;
}
}
}
اکنون، اجرای تستی ما برای آرایهی صحیح به کد زیر تبدیل میشود:
$size = 5;
$intArray = OneDimArrayFactory::Make(gettype(0), $size);
for ($i = 0; $i Size(); $i++) {
echo $intArray[$i] . ', ';
}
echo PHP_EOL;
for ($i = 0; $i Size(); $i++) {
$intArray[$i] = $i;
}
for ($i = 0; $i Size(); $i++) {
echo $intArray[$i] . ', ';
}
echo PHP_EOL;
echo $intArray[$size];
باز هم تمیزتر و زیباتر از آخرین کد قبلی. درست است؟ ما روند حرفهای تر کردن کد خودمان را در نسخههای بعدی ادامه خواهیم داد.
کد
<?php
namespace Techanic\CS\DS\Linear\Vector;
use Exception;
interface OneDimArrayInterface
{
function __construct(int $size);
function Size(): int;
}
abstract class AbstractOneDimArray implements OneDimArrayInterface, \ArrayAccess
{
/**
*
* @var int
*/
protected /*int*/ $_size = 0;
/**
*
* @var array
*/
protected /*array*/ $_values = [];
protected abstract function getValueType() : string;
protected abstract function getDefaultValue();
public function __construct(int $size)
{
if ($size < 0)
throw new Exception('Size must be positive number.');
$this->_size = $size;
$defaultValue = $this->getDefaultValue();
if (gettype($defaultValue) != $this->getValueType())
throw new Exception('Return type of the getDefaultValue() does not match getValueType().');
for ($i = 0; $i < $this->_size; $i++)
$this->_values[$i] = $defaultValue;
}
public function Size() : int
{
return $this->_size;
}
public function offsetSet($index, $value)
{
if (gettype($index) != gettype(0))
throw new Exception('The index must be of type ' . gettype(0));
if ($index < 0 || $index >= $this->_size)
throw new Exception('The index must be in the range (0, ' . ($this->_size -1) . ')');
if (gettype($value) != $this->getValueType())
throw new Exception('The value must be of type ' . $this->getValueType());
$this->_values[$index] = $value;
}
public function offsetExists($index)
{
if (gettype($index) != gettype(0))
throw new Exception('The index must be of type ' . gettype(0));
return ($index >=0 && $index < $this->_size);
}
public function offsetGet($index)
{
if (gettype($index) != gettype(0))
throw new Exception('The index must be of type ' . gettype(0));
return $this->_values[$index];
}
public function offsetUnset($index)
{
if (gettype($index) != gettype(0))
throw new Exception('The index must be of type ' . gettype(0));
if ($index < 0 || $index >= $this->_size)
return;
$this->_values[$index] = $this->getDefaultValue();
}
}
class IntOneDimArray extends AbstractOneDimArray
{
protected function getValueType() : string
{
return gettype(0);
}
protected function getDefaultValue()
{
return 0;
}
}
class FloatOneDimArray extends AbstractOneDimArray
{
protected function getValueType() : string
{
return gettype(1.0);
}
protected function getDefaultValue()
{
return 0.0;
}
}
class StringOneDimArray extends AbstractOneDimArray
{
protected function getValueType() : string
{
return gettype('');
}
protected function getDefaultValue()
{
return '';
}
}
class OneDimArrayFactory
{
public static function Make(string $type, int $size)
{
switch ($type)
{
case 'int':
case 'integer':
return new IntOneDimArray($size);
break;
case 'float':
case 'double':
return new FloatOneDimArray($size);
break;
case 'string':
default:
return new StringOneDimArray($size);
// throw new Exception('OneDimArray is not implemented for the type ' . $type);
break;
}
}
}
$size = 5;
$intArray = OneDimArrayFactory::Make(gettype(0), $size);
for ($i = 0; $i < $intArray->Size(); $i++) {
echo $intArray[$i] . ', ';
}
echo PHP_EOL;
for ($i = 0; $i < $intArray->Size(); $i++) {
$intArray[$i] = $i;
}
for ($i = 0; $i < $intArray->Size(); $i++) {
echo $intArray[$i] . ', ';
}
echo PHP_EOL;
echo $intArray[$size];