I am a self-taught programmer. I decided to learn design patterns by reading about them, and then coming up with their implementation in PHP. I know that learning would be virtually impossible without code review.
I will be grateful if you could review my implementation of the Memento design pattern and point to any shortcoming in the implementation of the pattern and general OOP principles.
My implementation tries to imitate the behaviour of a code repository (e.g. Git), where you can commit and checkout code based on previous commits. Below I present how a client can interact with the code.
/**
* Memento - a class that 'remembers' previous states
*/
class Repository
{
/**
* @var Commit[]
*/
private $commits = [];
/**
* Commit objects are pushed into an array
*/
public function saveCommit(Commit $commit)
{
array_push($this->commits, $commit);
}
/**
* Rewind code to a selected commit by the commit key
*
* @param integer $commitKey The index key of a commit to rewind code to
* @return string Rewinded code
*/
public function rewindCode($commitKey)
{
$rewindedCode = '';
for($i=0; $i <= $commitKey; $i++) {
$rewindedCode .= $this->commits[$i]->getCode();
}
return $rewindedCode;
}
public function getCommits()
{
return $this->commits;
}
/**
* Get all commited code so far
* @return string
*/
public function getCommitedCode()
{
$commitedCode = '';
foreach($this->commits as $commit) {
$commitedCode .= $commit->getCode();
}
return isset($commitedCode) ? $commitedCode : '';
}
}
-
/**
* A class/data structure that represents a single commit
*/
class Commit
{
private $timestamp;
private $code;
public function __construct($code)
{
$this->code = $code;
$this->timestamp = time();
}
public function getCode()
{
return $this->code;
}
}
-
/**
* Originator - a class that can save itself and retrieve states from the Memento object (Repository)
*/
class CodeBase
{
private $repository;
private $code;
public function __construct(Repository $repo)
{
$this->repository = $repo;
}
public function writeCode($code)
{
$this->code = $this->code . $code;
}
/**
* Extract code added since the last commit
* @return string Code that has not yet been commited
*/
private function getNewCode()
{
$commitedCode = $this->repository->getCommitedCode();
return $newCode = str_replace($commitedCode, '', $this->code);
}
/**
* Save state (code) to Memento (Repository)
*/
public function commitToRepository()
{
$newCode = $this->getNewCode();
$this->repository->saveCommit(new Commit($newCode));
}
/**
* Rewind code to the selected commit (Retrieve saved state)
*/
public function checkoutFromRepository($commitKey)
{
$this->code = $this->repository->rewindCode($commitKey);
}
public function getCode()
{
return $this->code;
}
}
-
$repo = new Repository();
$codeBase = new CodeBase($repo);
$codeBase->writeCode('First day code');
$codeBase->writeCode('Second day code');
$codeBase->commitToRepository();
$codeBase->writeCode('Third day code');
$codeBase->commitToRepository();
$codeBase->checkoutFromRepository(0); // Roll back to the first commit
echo $codeBase->getCode(); // Prints: "First day code Second day code"
$codeBase->checkoutFromRepository(1); // Roll back to the second commit
echo $codeBase->getCode(); // Prints: "First day code Second day code Third day code"