I have some handler ("controller") classes and they can process items in some way:
interface IHandler
{
public function execute(Item $item);
}
class FirstHandler implements IHandler
{
public function execute(Item $item) { echo $item->getTitle(); }
}
class SecondHandler implements IHandler
{
public function execute(Item $item) { echo $item->getId() . $item->getTitle(); }
}
class Item
{
public function getId() { return rand(); }
public function getTitle() { return 'title at ' . time(); }
}
But then I need to add some new functionality in child Item class:
class NewItem extends Item
{
public function getAuthor() { return 'author ' . rand(); }
}
and use it in SecondHandler
class SecondHandler implements IHandler
{
public function execute(Item $item) { printf('%d %s, author %s', $item->getId(), $item->getTitle(), $item->getAuthor()); }
}
But Item class actually has not getAuthor method. And, if I try to change signature of accept method in SecondHandler class, I will catch E_STRICT error about declaration compatibility. And, of course, it's sort of LSP violation.
How can I fix this problem? Do I need two interfaces, for example, INewHandler and IHandler, with different signatures of execute method? But it's some sort of code duplicates.
Also, I cannot use __constructor(Item $item) and __construct(NewItem $item) in handlers (and execute method without arguments), which will be seen like a better solution: they must be immutable and only single instance of every strategy allowed in application lifecycle.
As you discovered by yourself, the type hinting implementation of PHP has a lot limitations that make scenarios, like the one described by you, harder than they should be. In other typed languages like Java and Swift your implementation is absolutely licit.
After some thinking on your question I came to the solution presented by FĂ©lix but I consider it too much over engineered compared to the problem.
My answer to your question is not a solution but an advice that I give to you after years of development with PHP:
Give up with type hinting in PHP and develop like it should be... in a dynamic way.
PHP is more similar to Ruby/Python/JavaScript than Java/C++, and trying to copy 1 to 1 from static typed languages translates in forced and convolute implementations.
The solution to your implementation problem is easy, so don't over complicate it and keep it easy as it should be (KISS principle).
Declare the methods' arguments without the type and implement a check where you really need (for example throwing an exception).
interface IStrategy
{
public function execute($item);
}
class FirstStrategy implements IStrategy
{
public function execute($item) {
echo $item->getTitle();
}
}
class SecondStrategy implements IStrategy
{
public function execute($item) {
// execute(NewItem $item) is identical to this check.
if (! $item instanceof NewItem) {
throw new Exception('$item must be an instance of NewItem');
}
echo $item->getAuthor();
}
}
class Item
{
public function getId() { return rand(); }
public function getTitle() { return 'title at ' . time(); }
}
class NewItem extends Item
{
public function getAuthor() { return 'author ' . rand(); }
}
Again, don't think in Java but follow as much as possible the duck typing way.
When possible, try to don't strictly force the type of the parameters but adapt the behavior of the code based on the available interfaces (Duck Typing).
class SecondStrategy implements IStrategy
{
public function execute($item) {
$message = $item->getTitle();
// PHP 5 interface availability check.
if (is_callable([$item, 'getAuthor'])) {
$message .= ' ' . $item->getAuthor();
}
// With PHP 7 is even better.
// try {
// $message .= ' ' . $item->getAuthor();
// } catch (Error $e) {}
echo $message;
}
}
I hope to have helped you. ^_^
Both #daniele-orlando and #ihor-burlachenko made valid points.
Consider following approach for method overloading, which is kind of a compromise and should scale well:
interface IHandler
{
/**
* #param $item Item|NewItem
*/
public function execute($item);
// protected function executeItem(Item $item);
// protected function executeNewItem(NewItem $item);
}
trait IHandlerTrait
{
public function execute($item)
{
switch(true) {
case $item instanceof Item:
return $this->executeItem($item);
case $item instanceof NewItem:
return $this->executeNewItem($item);
default:
throw new \InvalidArgumentException("Unsupported parameter type " . get_class($item));
}
}
protected function executeItem(Item $item)
{
throw new \LogicException(__CLASS__ . " cannot handle execute() for type Item");
}
protected function executeNewItem(NewItem $item)
{
throw new \LogicException(__CLASS__ . " cannot handle execute() for type NewItem");
}
}
class FirstHandler implements IHandler
{
use IIHandlerTrait;
protected function executeItem(Item $item) { echo $item->getTitle(); }
}
class SecondHandler implements IHandler
{
use IIHandlerTrait;
// only if SecondHandler still need to support `Item` for backward compatibility
protected function executeItem(Item $item) { echo $item->getId() . $item-> getTitle(); }
protected function executeNewItem(NewItem $item) { printf('%d %s, author %s', $item->getId(), $item->getTitle(), $item->getAuthor()); }
}
Are you sure you want to use Strategy pattern here?
It looks, like the strategy's action here depends on the type of the element it processes. And Visitor pattern might apply here as well in this case.
As it stands, you appear to want to execute an extensible data record (Item and NewItem). Consider instead executing some pluggable behaviour (implemented via an interface).
It's hard from your writing to guess what that behavior would be, because (New)Item is just a glorified data structure in the example you provide.
If you want to work/manipulate with object in another object, you could/should use interfaces.
interface IStrategy
{
public function execute(ItemInterface $item);
}
interface ItemInterface
{
public function getTitle();
.....
}
If you want to extend public functionality of the (New)Item class you can make new interface for newItem
interface NewItemInterface extends ItemInterface
{
...
}
class SecondStrategy implements IStrategy
{
public function execute(NewItemInterface $item)
{ .... }
}
Or you can use some instance checks as others have mentioned.
If your inheritance and suggestion that SecondHandler should handle both Item and NewItem were correct in the first place, then you should be able to hide this functionality behind the common interface. From your examples, it might be called toString() which might be a part the Item interface.
Otherwise, there might be something wrong with your design initially. And you have to change your inheritance or the way you handle items. Or something else we don't know about.
Also, I don't know why you need DTO but it seems there is some misunderstanding of Doctrine. Doctrine is an ORM and it solves your persistence problem. It adds limitations on how you communicate with your storage introducing repositories, but it doesn't define your domain logic.
As per the Interface segregation please find some solution.
```
# based on interface segrigation.
interface BasicInfo
{
public function getId();
public function getTitle();
}
interface AuthorInfo
{
public function getAuthor();
}
interface IHandler
{
public function execute(Item $item);
}
class FirstHandler implements IHandler
{
public function execute(Item $item) { echo $item->getTitle(); }
}
class SecondHandler implements IHandler
{
public function execute(Item $item) { echo $item->getId() . $item->getTitle(); }
}
class Item implements BasicInfo
{
public function getId() { return rand(); }
public function getTitle() { return 'title at ' . time(); }
}
class Item2 extends Item implements AuthorInfo
{
public function getAuthor() { return 'author ' . rand(); }
}
But I think you should not kept the dependency of the Item class. you should write some duplicated code to keep the class pluggable/independent. So the Open/close principle should also there.
Related
I'm currently writing a small console application on the Symfony 2 framework. I'm attempting to insulate the application from the framework (mainly as an exercise after hearing some interesting talks on hexagonal architecture/ports and adaptors, clean code and decoupling applications from frameworks), so that it could potentially be run as a console application, a web application, or moved to another framework with little effort.
The issue I'm having is when one of my interfaces is implemented using the adaptor pattern and it depends on another interface that is also implemented using the adaptor pattern. It's difficult to describe and is probably best described with a code example. Here I've prefixed my class/interface names with "My", just to make it clear which code is my own (and I can edit) and which belongs to the Symfony framework.
// My code.
interface MyOutputInterface
{
public function writeln($message);
}
class MySymfonyOutputAdaptor implements MyOutputInterface
{
private $output;
public function __construct(\Symfony\Component\Console\Output\ConsoleOutput $output)
{
$this->output = $output;
}
public function writeln($message)
{
$this->output->writeln($message)
}
}
interface MyDialogInterface
{
public function askConfirmation(MyOutputInterface $output, $message);
}
class MySymfonyDialogAdaptor implements MyDialogInterface
{
private $dialog;
public function __construct(\Symfony\Component\Console\Helper\DialogHelper $dialog)
{
$this->dialog = $dialog;
}
public function askConfirmation(MyOutputInterface $output, $message)
{
$this->dialog->askConfirmation($output, $message); // Fails: Expects $output to be instance of \Symfony\Component\Console\Output\OutputInterface
}
}
// Symfony code.
namespace Symfony\Component\Console\Helper;
class DialogHelper
{
public function askConfirmation(\Symfony\Component\Console\Output\OutputInterface $output, $question, $default = true)
{
// ...
}
}
One extra thing to note is that \Symfony\Component\Console\Output\ConsoleOutput implements \Symfony\Component\Console\Output\OutputInterface .
To conform to MyDialogInterface, the MySymfonyDialogAdaptor::askConfirmation method must take an instance of MyOutputInterface as an argument. However, the call to Symfony's DialogHelper::askConfirmation method expects an instance of \Symfony\Component\Console\Output\OutputInterface, meaning the code won't run.
I can see a couple of ways around this, neither of which are particularly satisfactory:
Have MySymfonyOutputAdaptor implement both MyOutputInterface and Symfony\Component\Console\Output\OutputInterface. This isn't ideal, as I'd need to specify all of the methods in that interface, when my application only really cares about the writeln method.
Have MySymfonyDialogAdaptor assume that the object passed to it is an instance of MySymfonyOutputAdaptor: If it's not, then throw an exception. Then add a method to the MySymfonyOutputAdaptor class to obtain the underlying \Symfony\Component\Console\Output\ConsoleOutput object, which can be passed to Symfony's DialogHelper::askConfirmation method directly (as it implements Symfony's OutputInterface). This would look something like the following:
class MySymfonyOutputAdaptor implements MyOutputInterface
{
private $output;
public function __construct(\Symfony\Component\Console\Output\ConsoleOutput $output)
{
$this->output = $output;
}
public function writeln($message)
{
$this->output->writeln($message)
}
public function getSymfonyConsoleOutput()
{
return $this->output;
}
}
class MySymfonyDialogAdaptor implements MyDialogInterface
{
private $dialog;
public function __construct(\Symfony\Component\Console\Helper\DialogHelper $dialog)
{
$this->dialog = $dialog;
}
public function askConfirmation(MyOutputInterface $output, $message)
{
if (!$output instanceof MySymfonyOutputAdaptor) {
throw new InvalidArgumentException();
}
$symfonyConsoleOutput = $output->getSymfonyConsoleOutput();
$this->dialog->askConfirmation($symfonyConsoleOutput, $message);
}
}
This feels wrong: If MySymfonyDialogAdaptor::askConfirmation has a requirement that its first argument is an instance of MySymfonyOutputAdaptor, it should specify it as its typehint, but that would mean it no longer implements MyDialogInterface. Also, accessing the underlying ConsoleOutput object outside of its own adaptor doesn't seem ideal, as it should really be wrapped by the adaptor.
Can anyone suggest a way around this? I feel like I'm missing something: Perhaps I'm putting adaptors in the wrong places and rather than multiple adaptors, I just need one adaptor wrapping the whole output/dialog system? Or maybe there's another inheritance layer I need to include in order to implement both interfaces?
Any advice appreciated.
EDIT: This issue is very similar to the one described in the following pull-request: https://github.com/SimpleBus/CommandBus/pull/2
After much discussion with colleagues (thanks Ian and Owen), plus some help from Matthias via https://github.com/SimpleBus/CommandBus/pull/2 , we've come up with the following solution:
<?php
// My code.
interface MyOutputInterface
{
public function writeln($message);
}
class SymfonyOutputToMyOutputAdaptor implements MyOutputInterface
{
private $output;
public function __construct(\Symfony\Component\Console\Output\OutputInterface $output)
{
$this->output = $output;
}
public function writeln($message)
{
$this->output->writeln($message)
}
}
class MyOutputToSymfonyOutputAdapter implements Symfony\Component\Console\Output\OutputInterface
{
private $myOutput;
public function __construct(MyOutputInterface $myOutput)
{
$this->myOutput = $myOutput;
}
public function writeln($message)
{
$this->myOutput->writeln($message);
}
// Implement all methods defined in Symfony's OutputInterface.
}
interface MyDialogInterface
{
public function askConfirmation(MyOutputInterface $output, $message);
}
class MySymfonyDialogAdaptor implements MyDialogInterface
{
private $dialog;
public function __construct(\Symfony\Component\Console\Helper\DialogHelper $dialog)
{
$this->dialog = $dialog;
}
public function askConfirmation(MyOutputInterface $output, $message)
{
$symfonyOutput = new MyOutputToSymfonyOutputAdapter($output);
$this->dialog->askConfirmation($symfonyOutput, $message);
}
}
// Symfony code.
namespace Symfony\Component\Console\Helper;
class DialogHelper
{
public function askConfirmation(\Symfony\Component\Console\Output\OutputInterface $output, $question, $default = true)
{
// ...
}
}
I think the concept I was missing was that adaptors are essentially one-directional (e.g. from my code to Symfony's, or vice versa) and that I needed another separate adaptor to convert from MyOutputInterface back to Symfony's OutputInterface class.
This isn't completely ideal, as I still have to implement all of Symfony's methods in this new adaptor (MyOutputToSymfonyOutputAdapter), but this architecture does feel quite well-structured, as it is clear that each adaptor converts in one direction: I've renamed the adaptors accordingly to make this more clear.
Another alternative would be to fully implement only the methods that I wanted to support (just writeln in this example) and define the other methods to throw an exception to indicate they are unsupported by the adaptor if they are called.
Many thanks for the help all.
I am testing a class that has a dependency injected. In my infinite cleverness, I scattered references to the dependency's class constant throughout my code.
(Sorry for the stupid example, wanted to deviate from foobar.)
<?php
class Book_PhoneBook extends Book {
private $author;
public function __construct(Author $author) {
$this->author = $author;
}
public function getCover() {
return $this->author->getTitle(Author::NAME_UNKNOWN) . ' - YELLOW PAGES';
}
}
class Author {
const NAME_UNKNOWN = 'anonymous';
public function getTitle($nameStyle) {
//do complicated calculation of author name
if ($nameStyle == $this::NAME_UNKNOWN) {
$name = "Anonymous";
}
//do further complicated calculations, modifying $name
return $name;
}
}
My problem is with unit tests! I'm getting "Fatal error: Undefined class constant 'NAME_UNKNOWN'" when I mock Author. The whole purpose of mocking is to avoid including the Author class file, so what is the correct way to handle that constant?
I could split Author into several objects or split the method that relies on the constant, but that would incur much repetition.
The fact that you are using the class constant from Author means that you are dependent on having that class available in your code and thus also in your test. So in order to test Book_PhoneBook you need to have an Author class with the constant. If the class constant is going to be a series of flags that you will need to pass to your class, you will need to have the class available and in your test make sure that the original class is loaded. You are dependent on that class for Book_PhoneBook::getCover().
You can also just use the string value of the class constant in your Book_PhoneBook though that sort of defeats the purpose of making it a constant so that you are able to change the value in one place.
With your example, it is a little tricky to get an idea of what you are trying to achieve but since you want to remove the dependency on Author in Book_PhoneBook, I would change it to something like this:
<?php
class Book_PhoneBook extends Book {
private $author;
public function __construct(Author $author) {
$this->author = $author;
}
public function getCover() {
return $this->author->getTitle() . ' - YELLOW PAGES';
}
}
class Author {
const NAME_UNKNOWN = 'anonymous';
public function __construct($nameStyle) {
$this->nameStyle = $nameStyle;
}
public function getTitle() {
//do complicated calculation of author name
if ($this->nameStyle == $this::NAME_UNKNOWN) {
$name = "Anonymous";
}
//do further complicated calculations, modifying $name
return $name;
}
}
This solution does have the problem that adding other options is difficult for getTitle. And without having a better idea of what you are trying to achieve with getTitle, I am not able to offer a better solution.
For anybody having a problem like this in the future, I ended up with this based on #Schleis' answer:
class Author {
const NAME_UNKNOWN = 'anonymous';
private function getTitle($nameStyle) {
//do stuff, taking $nameStyle into account
return $name;
}
public function getAnonymousTitle() {
return $this->getTitle($this::NAME_UNKNOWN);
}
}
I have a reoccuring problem that I am currently tackling like so -
a POST variable coming in to the script which has a platform, the platform is from a list such as: xbox,ps3,pc,mobileapp,mobilegame etc
for each different platform I want to be able to do something different in my script but in some cases I want code to do very similar things at the moment I do something like this:
$platformArray = array(
'ps3'=>array('displayName'=>'playstation 3','function'=>'funcPS3'),
'xbox'=>array('displayName'=>'Xbox','function'=>'funcXbox')
)
//similar amongst all platforms code on line below
echo 'you have a :'.$platformArray[$_POST['platform']]['displayName'].' for playing games';
call_user_func($platformArray[$_POST['platform']['function']);
function funcPS3(){
echo 'ps3 specific code';
}
function funcXbox(){
echo 'xbox specific code';
}
I want to move towards a OOP approach in my code, I want to use objects as my data storage medium rather than arrays as I'm doing now, but I do sometimes need to define attributes in the code ahead of time, how could I do the above but with objects?
I would recommend for you to start by understanding polymorphism. This lecture should be good start.
When you are trying to create behavior, based on some flag, you should implement two classes with same interface:
class Xbox
{
private $displayName = 'XBox 360';
public function identify()
{
// Xbox-specific stuff
return ':::::::::::'. $this->displayName;
}
}
class PS3
{
private $displayName = 'Playstation 3';
public function identify()
{
// playstation-specific stuff
return '+++'. $this->displayName . '+++';
}
}
The two classes have method with same name that would do different things;
$platform = $_POST['platform'];
// classes in PHP are case-insensitive
// expected values would be: xbox, Xbox, ps3, pS3
if ( !class_exists($platform) )
{
echo "Platform '{$platform}' is not supported";
exit;
// since continuing at this point would cause a fatal error,
// better to simply exit
}
$object = new $platform;
echo $object->identify();
Basically, in this case you really do not care, which type of platform you are working with. All you need to know is that they both have same public interface. This is called "polymorphic behavior".
I'm going to work from a very naive OO version, to what is considered "good" OO code, using polymorphic behavior and avoiding global state.
1. Not polymorphic and has global static data
This is pretty bad because it is really just a wrapper object over procedural code. It needs a map of functions to call for each type of platform.
class Platform {
private static $platformArray = array(
'ps3' => array(
'displayName'=>'playstation 3',
'function'=>'funcPS3'
),
'xbox' => array(
'displayName'=>'Xbox',
'function'=>'funcXbox'
)
);
private $type;
public function __construct($type) {
if (!array_key_exists($type, self::$platformArray)) {
throw new Exception("Invalid Platform type $type" );
}
$this->type = $type;
}
public function printCode() {
// This was a question embedded within your question, you can use
// http://php.net/manual/en/function.call-user-func.php
// and pass an instance with a method name.
return call_user_func( array($this, self::$platformArray[$this->type]) );
}
private function funcPS3(){
echo 'ps3 specific code';
}
private function funcXbox(){
echo 'xbox specific code';
}
}
$plat = new Platform($_POST['platform']);
$plat->printCode();
2. Polymorphic... but it still uses global data
By creating a base class you can implement behavior in subclasses, creating separate class for each concern. The big problem here is that subclasses need to register with a global registry.
abstract class Platform {
abstract protected function getCode();
public function printCode() {
echo $this->getCode();
}
private function __construct() {} // so only factory can instantiate it
private static $platformArray = array();
public static function create($type) {
if (!array_key_exists($type, self::$platformArray)) {
throw new Exception("Invalid Platform type $type" );
}
return new self::$platformArray[$type];
}
public static function addPlatform($type, $ctor) {
if (!is_subclass_of($ctor, 'Platform')) {
throw new Exception("Invalid Constructor for Platform $ctor" );
}
self::$platformArray[$type] = $ctor;
}
}
class PlatformXBox extends Platform{
protected function getCode() {
return 'xbox specific code';
}
}
Platform::addPlatform('xbox', 'PlatformXBox');
class PlatformPs3 extends Platform {
protected function getCode() {
return 'ps3 specific code';
}
}
Platform::addPlatform('ps3', 'PlatformPs3');
$plat = Platform::create($_POST['platform']);
$plat->printCode();
3. Polymorphic, no global data
By putting your code into a namespace, you avoid the static code in the base class and avoid the dangers of mapping post parameters directly into classes.
namespace platform {
interface IPlatform {
public function getDisplayName();
public function getCode();
}
class PlatformFactory {
static public function create($platformType) {
$className = "\\platform\\$platformType";
if ( !is_subclass_of($className, "\\platform\\IPlatform") ){
return null;
}
return new $className;
}
}
class Xbox implements IPlatform {
public function getDisplayName(){
return 'xbox';
}
public function getCode(){
return 'xbox code';
}
}
class Ps3 implements IPlatform {
public function getDisplayName(){
return 'ps3';
}
public function getCode(){
return 'ps3 code';
}
}
}
Now you can use those classes like the following
$platform = platform\PlatformFactory::create('xbox');
echo $platform->getCode() ."\n" ;
$platform2 = platform\PlatformFactory::create('ps3');
echo $platform2->getDisplayName()."\n";
$noPlatform = platform\PlatformFactory::create('dontexist');
if ($noPlatform) {
echo "This is bad, plaftorm 'dontexist' shouldn't have been created";
} else {
echo "Platform 'dontexist' doesn't exist";
}
You might want to create a class called platforms and within the class a different method for each platform:
class platforms {
//Create your variables here, also called properties.
public $displayName;
//Create a function, also called a method for each platform you intent to use.
public function xboxPlatform(){
//Code comes here what you want to do.
}
}
Hope this helps.
I'm trying to solve a design puzzle in the most efficient way, but I tend to end thinking that I really need a multiple inheritance here. So, here I am, asking the pros. I'm creating an active-record lib that will perform almost identical actions with different name and value variables. I'm on PHP 5.2.13
Suppose I have these classes:
class Property {
protected $_props = array();
function set($name, $value) {
$this->_props[$name] = $value;
};
}
class ColorProperties extends Property {
function red($value) {
$this->set('red', $value);
}
function blue($value) {
$this->set('blue', $value);
}
}
class LayoutProperties extends Property {
function x($value) {
$this->set('x', $value);
}
function y($value) {
$this->set('y', $value);
}
}
Now I need to create a class ShapeProperties that will inherit from both, ColorProperties and LayoutProperties. So that:
$shape = new ShapeProperties();
$shape->x(10);
$shape->red(255);
The reason why I need it that way, is to have auto-completion in IDE with huge PHPDoc comment block for each property. So, seems that I'm leaning towards copy/paste out of despair.
OLD BODY (BEFORE EDITS)
I've got these classes (blocks):
class Red {
function red();
}
class LightRed {
function light_red();
}
class Blue {
function blue();
}
class LightBlue {
function light_blue();
}
class Green {
function green();
}
class LightGreen {
function light_green();
}
And now I need to build a numerous amount of classes using these blocks, ie:
class RGB extends Red, Green, Blue {
function red();
function blue();
function green();
}
class RLRB extends Red, LightRed, Blue {
function red();
function light_red();
function blue();
}
So if I switch word class with interface, I'll get what I need, but I need working implementation without loads of boilerplate code. Is there a way/approach to work this around in PHP?
You can use magic functions and delegation to do something similar. I hope your real case is not really about colors, because this would be a bit overkill to avoid a few one line functions.
You can provide an arbitrary list of implementations and cycle through them until you find one that has the appropriate method name. Call it with all of the arguments. There can be method clashes, just like it happens with multiple inheritance, except that here, no compiler will tell you. You also lose the argument validation and such.
class Color
{
private $implementations;
function __construct($implementations)
{
$this->implementations = $implementations;
}
function __call($functionName, $args)
{
foreach ($this->implementations as $impl) {
$callback = array($impl, $functionName);
if (is_callable($callback)) {
return call_user_func_array($callback, $args);
}
}
throw new Exception("Method not found: $functionName");
}
}
class RGB
{
function __construct()
{
parent::__construct(array(
new Red,
new Green,
new Blue,
));
}
}
$color = new RGB;
$color->red();
In the near future it will be possible with php 5.4 new feature - traits. This will look like this:
trait RED{
function red(){ };
}
trait BLUE{
function blue(){ };
}
trait GREEN{
function green(){ };
}
class RGB{
use RED,GREEN,BLUE;
}
But for now, the best method seems to be using static methods and dependency injections, like this:
class RED{
static function get_red($obj){
return '0x'.dechex($obj->red);
}
}
class RGB{
public $red;
public $green;
public $blue;
public function __construct($r,$g,$b){
$this->red = $r;
$this->green = $g;
$this->blue = $b;
}
public function get_red(){
return RED::get_red($this);
}
}
$rgb = new RGB(255,255,255);
echo $rgb->get_red();
but you still have to declare all functions and make it call method from other class.
Here is an implementation example of the algorigthm in the base absctract class from http://sourcemaking.com/design_patterns/template_method/php
public final function showBookTitleInfo($book_in) {
$title = $book_in->getTitle();
$author = $book_in->getAuthor();
$processedTitle = $this->processTitle($title);
$processedAuthor = $this->processAuthor($author);
if (NULL == $processedAuthor) {
$processed_info = $processedTitle;
} else {
$processed_info = $processedTitle.' by '.$processedAuthor;
}
return $processed_info;
}
I don't like it becase I think, that "showBookTitleInfo" knows too much about the methods it calls.
Here is another example
abstract class template_method {
var $state;
public function __construct() {
$this->state = 0;
}
public function processEvent( $event ) {
$this->doFirstStep( $event );
$this->doSecondStep( $event );
}
abstract public function doFirstStep( &$event );
abstract public function doSecondStep( &$event );
}
class CustomLogic extends template_method {
public function doFirstStep( &$event ) {
echo __METHOD__.": state: ".$this->state." event: $event\n";
$this->state++;
}
public function doSecondStep( &$event ) {
echo __METHOD__.": state: ".$this->state." event: $event\n";
$this->state++;
}
}
why we pass event as by-reference, if we don't change its value?
How should I implement "my steps" logic, if they are using current state, can modify its value, and other steps can read modified value and can modify it too?
For example, I want to implement cost counting mechanism for scheduled message sending - simple and reccurent(ex: every Mon, Fri until 23.05.2009).
So, I implement the algorithm in abstract class as following:
abstract class AbstractCostCounter {
public function countNotReccurentSendingCost($messageObj) {
$totalMessages = $messageObj->getTotalMessages(); // multiple recipients are allowed
$message_cost = 1; // just to give you an idea
$this->cost = $totalMessages * $message_cost;
}
abstract public function countOptional();
// I pass $messageObject not as by-reference, because it hasn't to be modified
public function countCost( $messageObject ) {
$this->countNotReccurentSendingCost( $messageObject );
$this->countOptional( $messageObject );
}
}
class TemplateNotReccurentCostCounting {
public function countOptional($messageObj) {
// do nothing
}
}
class TemplateReccurentCostCounting {
public function countOptional($messageObj) {
$notReccurentSendingCost = $this->cost;
$totalMessagesInScheduledPlan = $messageObj->getTotalMessagesInScheduledPlan();
$reccurentSendingPlanCost = $notReccurentSendingCost * $totalMessagesInScheduledPlan;
$this->cost = $reccurentSendingPlanCost;
}
}
Am I moving in the right direction?
Is it where Template method design pattern should be implemented?
Please let me know, if it is something wrong with this code.
P.S. cost counter is not a production code. I wrote it because I wanted to give you an idea.
Thanks, in advance
The template method pattern gives the parent class a lot of control, the parent class has to know a lot about the abstract methods (their signature) because it has to 'control' the algorithm. BTW the concrete method in the parent class has to be final.
You have no advantage with your firstStep secondStep methods, I could implement what I want in stepOne and do nothing in stepTwo...
The question is when would you want to use Template Method Pattern, not how to rewrite it to give more flexibility :)