A two array data structure implementation in PHP, version 3
In previous versions of two dimentional array data structure in PHP programming language, we developed a data structure called AbstractTwoDimArray
and used ArrayAccess
interface to use [] operator in our array data structure.
In this version, we make more from interface concept and also use a new tool. In fact we are going to use a famous design pattern called Factory. We implmented a factory to use in our array data strucrue execution test.
We also derive two other concrete calsses FloatTwoDimArray
and StringTwoDimArray
from our base class.
The first step is how to use interface concept. We implement an interface to force array data structures to implement Size
method.
interface TwoDimArrayInterface
{
function __construct(int $rows, int $sols);
function Size() : array; // [rows, cols]
}
Then in our case, we use this interface as well.
abstract class AbstractTwoDimArray implements TwoDimArrayInterface, \ArrayAccess
You noticed that we used two interface at the same time. This is valid in PHP and is very helpful also.
The base class implementation remains untouched despite this tiny change.
Now, we are going to implement more simple arrays, for float data type and string data type as well.
class FloatTwoDimArray extends AbstractTwoDimArray
{
protected function getValueType() : string
{
return gettype(1.0);
}
protected function getDefaultValue()
{
return 0.0;
}
}
class StringTwoDimArray extends AbstractTwoDimArray
{
protected function getValueType() : string
{
return gettype('');
}
protected function getDefaultValue()
{
return '';
}
}
One again, you see that we need only to implement two methods: getValueType
and getDefaultValue
. This is the power of OOP (object-oriented programming).
Now, as we promised, we are going to use factory design pattern to make neater execution test. We implement TwoDimArrayFactory
class like this:
class TwoDimArrayFactory
{
public static function Make(string $type, int $rows, int $cols)
{
switch ($type)
{
case 'int':
case 'integer':
return new IntTwoDimArray($rows, $cols);
break;
case 'float':
case 'double':
return new FloatTwoDimArray($rows, $cols);
break;
case 'string':
return new StringTwoDimArray($rows, $cols);
break;
default:
throw new Exception('TwoDimArray is not implemented for the type ' . $type);
break;
}
}
}
Now, our execution test for int array turns into this code:
$rows = 5;
$cols = 5;
$intMatrix = TwoDimArrayFactory::Make(gettype(0), $rows, $cols);
for ($row = 0; $row Size()[0]; $row++) {
for ($col = 0; $col Size()[1]; $col++)
echo $intMatrix[$row][$col] . ' ';
echo PHP_EOL;
}
echo PHP_EOL;
for ($row = 0; $row Size()[0]; $row++) {
for ($col = 0; $col Size()[1]; $col++)
$intMatrix[$row][$col] = $row + $col;
}
for ($row = 0; $row Size()[0]; $row++) {
for ($col = 0; $col Size()[1]; $col++)
echo $intMatrix[$row][$col] . ' ';
echo PHP_EOL;
}
echo PHP_EOL;
Even neater and more beautiful from the last one, yeah? We keep track of making our code more professional in the next versions.
Code
<?php
namespace Techanic\CS\DS\Linear\Vector;
include_once '../1- One-dimensional array (Simple array)/one-dimensional-array.php';
use Exception;
interface TwoDimArrayInterface
{
function __construct(int $rows, int $sols);
function Size() : array; // [rows, cols]
}
abstract class AbstractTwoDimArray implements TwoDimArrayInterface, \ArrayAccess
{
/**
*
* @var array
*/
private /*array*/ $_size = [0, 0];
/**
*
* @var array
*/
private /*array*/ $_rows = [];
protected abstract function getValueType() : string;
protected abstract function getDefaultValue();
public function __construct(int $rows, int $cols)
{
if ($rows < 0)
throw new Exception('rows must be positive number.');
if ($cols < 0)
throw new Exception('cols must be positive number.');
$this->_size = [$rows, $cols];
$defaultValue = $this->getDefaultValue();
if (gettype($defaultValue) != $this->getValueType())
throw new Exception('Return type of the getDefaultValue() does not match getValueType().');
for ($row = 0; $row < $this->_size[0]; $row++)
{
$this->_rows[$row] = OneDimArrayFactory::Make($this->getValueType(), $this->_size[1]);
for ($col = 0; $col < $this->_size[1]; $col++)
($this->_rows[$row])[$col] = $defaultValue;
}
}
public function Size(): array
{
return $this->_size;
}
public function offsetSet($row, $value)
{
if (gettype($row) != gettype(0))
throw new Exception('The index must be of type ' . gettype(0));
if ($row < 0 || $row >= $this->_size[0])
throw new Exception('The index must be in the range (0, ' . ($this->_size[0] -1) . ')');
if (!($value instanceof OneDimArrayInterface)) // TODO: check more accurate constraint
throw new Exception('The value must be of type OneDimArrayInterface');
if ($value->Size() != $this->_size[1])
throw new Exception('The value size does not match.');
$this->_rows[$row] = $value;
}
public function offsetExists($row)
{
if (gettype($row) != gettype(0))
throw new Exception('The index must be of type ' . gettype(0));
return ($row >=0 && $row < $this->_size[0]);
}
public function offsetGet($row)
{
if (gettype($row) != gettype(0))
throw new Exception('The index must be of type ' . gettype(0));
return $this->_rows[$row];
}
public function offsetUnset($row)
{
if (gettype($row) != gettype(0))
throw new Exception('The index must be of type ' . gettype(0));
$this->_rows[$row] = OneDimArrayFactory::Make($this->getValueType, $this->_size[1]);
}
}
class IntTwoDimArray extends AbstractTwoDimArray
{
protected function getValueType() : string
{
return gettype(0);
}
protected function getDefaultValue()
{
return 0;
}
}
class FloatTwoDimArray extends AbstractTwoDimArray
{
protected function getValueType() : string
{
return gettype(1.0);
}
protected function getDefaultValue()
{
return 0.0;
}
}
class StringTwoDimArray extends AbstractTwoDimArray
{
protected function getValueType() : string
{
return gettype('');
}
protected function getDefaultValue()
{
return '';
}
}
class TwoDimArrayFactory
{
public static function Make(string $type, int $rows, int $cols)
{
switch ($type)
{
case 'int':
case 'integer':
return new IntTwoDimArray($rows, $cols);
break;
case 'float':
case 'double':
return new FloatTwoDimArray($rows, $cols);
break;
case 'string':
return new StringTwoDimArray($rows, $cols);
break;
default:
throw new Exception('TwoDimArray is not implemented for the type ' . $type);
break;
}
}
}
$rows = 5;
$cols = 5;
$intMatrix = TwoDimArrayFactory::Make(gettype(0), $rows, $cols);
for ($row = 0; $row < $intMatrix->Size()[0]; $row++) {
for ($col = 0; $col < $intMatrix->Size()[1]; $col++)
echo $intMatrix[$row][$col] . ' ';
echo PHP_EOL;
}
echo PHP_EOL;
for ($row = 0; $row < $intMatrix->Size()[0]; $row++) {
for ($col = 0; $col < $intMatrix->Size()[1]; $col++)
$intMatrix[$row][$col] = $row + $col;
}
for ($row = 0; $row < $intMatrix->Size()[0]; $row++) {
for ($col = 0; $col < $intMatrix->Size()[1]; $col++)
echo $intMatrix[$row][$col] . ' ';
echo PHP_EOL;
}
echo PHP_EOL;