code samples: https://sourcemaking.com/design_patterns/composite/php I made something similar, except that "OnTheBookShelf" knows about $books (SeveralBooks). My "boss" say its bad that they know about each other. But why?
Allright, I edit it:
abstract class OnTheBookShelf {
public $shelf; /////////////////////////////////////////////////
abstract function getBookInfo($previousBook);
abstract function getBookCount();
abstract function setBookCount($new_count);
abstract function addBook($oneBook);
abstract function removeBook($oneBook);
}
class OneBook extends OnTheBookShelf {
private $title;
private $author;
function __construct($title, $author) {
$this->title = $title;
$this->author = $author;
}
function getBookInfo($bookToGet) {
if (1 == $bookToGet) {
return $this->title." by ".$this->author;
} else {
return FALSE;
}
}
function getBookCount() {
return 1;
}
function setBookCount($newCount) {
return FALSE;
}
function addBook($oneBook) {
return FALSE;
}
function removeBook($oneBook) {
return FALSE;
}
}
class SeveralBooks extends OnTheBookShelf {
private $oneBooks = array();
private $bookCount;
public function __construct() {
$this->setBookCount(0);
}
public function getBookCount() {
return $this->bookCount;
}
public function setBookCount($newCount) {
$this->bookCount = $newCount;
}
public function getBookInfo($bookToGet) {
if ($bookToGet <= $this->bookCount) {
return $this->oneBooks[$bookToGet]->getBookInfo(1);
} else {
return FALSE;
}
}
public function addBook($oneBook) {
$oneBook->shelf = $this; //////////////////////////////////////////////////
$this->setBookCount($this->getBookCount() + 1);
$this->oneBooks[$this->getBookCount()] = $oneBook;
return $this->getBookCount();
}
public function removeBook($oneBook) {
$counter = 0;
while (++$counter <= $this->getBookCount()) {
if ($oneBook->getBookInfo(1) ==
$this->oneBooks[$counter]->getBookInfo(1)) {
for ($x = $counter; $x < $this->getBookCount(); $x++) {
$this->oneBooks[$x] = $this->oneBooks[$x + 1];
}
$this->setBookCount($this->getBookCount() - 1);
}
}
return $this->getBookCount();
}
}
I added a bunch of //////////////// to the problematic lines. And here they say that book has reference to shelf.
They should know about their interfaces, so that they can be changed.
Let's say you have classes:
Bookshelf
private books: Book[]
Book
public title: String
You would then access books[i].title and display title of the a book.
Now imagine that the programmer in charge of Book decides that the title merrits its own class, so we have:
Book
public title: Title
Title
private t: String
public toString()
Now the programmer who was coding Bookshelf is required to change their code.
On the other hand if we would have:
Book
private title: String
public getTitleString()
Then we could change the implementation of the Book class, and all we would have to do is to write the getTitleString() function that would return a string representation of a title, and everything would continue to work without any additional changes to Bookshelf.
My "boss" say its bad that they know about each other. But why?
The problem is circular reference between Shelf and Book that is not so trivial and requires special care to work with.
For example, if you simply write $oneBook->shelf = $this; inside addBook method, then what happens if consumer calls this method for one book on two different shelves?
$book = new Book;
$shelf1 = new Shelf;
$shelf2 = new Shelf;
$shelf1->addBook($book);
$shelf2->addBook($book);
Book will be added to both shelves, but it will hold reference only to the last shelf, which leads to inconsistency and potential run-time bugs.
Of cause, circular reference can be done right, but it requires special attention and adds complexity to code.
Related
Let's say you have a class like:
class Foo
{
// maybe private or protected?
public Pages $pages;
public Owners $owners;
// ...
}
How can I implement methods that would accept closures in a chained fashion? Every method call in the chain should update the internal class properties (so they are using the latest).
Usage examples:
$pagesArray = Foo::wherePage('page', function ($page, $owners) {
// do something with the given page, owners is optional
})->b(function ($owners) {
// now do something with owners, e.g. $owners->foo();
})->getPagesArray();
Or
$pagesArray = Foo::pages(function ($pages, $owners) {
// loop pages, owners is optional
})->b(function ($owners) {
// now do something with owners, e.g. $owners->foo();
})->getPagesArray();
Thanks!
<?php
class Foo
{
protected $pages = ['abc'];
public static function setPages($pages)
{
call_user_func($pages, ['aaa']);
$thisClass = new static();
return $thisClass;
}
public function owners($owners)
{
call_user_func($owners, ['john', 'wick']);
return $this;
}
public function getPages()
{
return $this->pages;
}
}
$Foo = Foo::setPages(function($pages) {
var_dump($pages);
})->owners(function($owners) {
var_dump($owners);
})->getPages();
var_dump($Foo);
So I have 2 classes: Lets call them Person and Car
I need to access some attributes from my person object in my Car class before I can instantiate.
Do I simply say something to the order of: $car = new Car($person);
If yes, then how do I access those object attributes in my Car class? Would it be something like this:
class Car{
function __construct($person)
{
$this->person = $person;
}
}
If no, what would be a way to achieve this?
We have some confusion here. Real world OOP, consider and compare:
A car is given to or bought by a person:
$person->addCar($car);
A person is entering the car (to sit on the left front seat):
$car->addPerson($person, Car::SEAT_FRONT_LEFT);
Where Car::SEAT_FRONT_LEFT is a public const member of Car.
The relations are important to be kept semantically correct to be able to construct working objects.
--
To implement this, it might be helpful to lookup the meaning of (Aware-)Interface.
Example classes I'd probably define:
interface Person {
public function getHeight();
public function setHeight();
}
class CarOwner implements Person {
protected $height;
protected $cars = array();
public function getHeight();
public function setHeight();
public function getCars();
};
class Driver implements Person {
protected $height;
public function getHeight();
public function setHeight();
};
class Car {
protected $persons = array(); // #var array
protected $readyToDrive = false;
public const SEAT_FRONT_LEFT = 1;
public const SEAT_FRONT_RIGHT = 2;
// ...
public function addPerson(Person $person, $seat) {
if ($person instanceof Driver) {
$this->isReadyToDrive(true);
}
// I know this is stupid but you get the point:
if ($this->getDriver()->getHeight() < 140 && $this->getSeat()->getPosition() > 40) {
throw new Exception('please set the seat to fit your height!');
}
if (!in_array($person, $this->persons)) {
$this->persons[$seat] = $person;
}
else {
throw new Exception('person is already in the car!');
// Person first needs to "leave" the car! like:
// $car->removePerson($person)->addPerson($person, Car::SEAT_REAR_LEFT);
}
}
public function getDriver() {
if (isset($persons[self::SEAT_FRONT_LEFT]) && $persons[self::SEAT_FRONT_LEFT] instanceof Driver) {
return persons[self::SEAT_FRONT_LEFT];
}
else { //... }
}
};
If we want to see it from encapsulation side, we need to think about it. Is it ok, if a car knows a Person, which is inside? Maybe we need a Service/Controller which takes this params?
function processSomething(Person $person, Car $car) {}
But your example is not bad at all. It is depended on the usecase. Its the way it is done, if a car may know about his person.
If you can have multiple persons, you can have this constructor:
public function __construct(array $personList) {
//Check for a person
if (count($personList) < 1) {
throw new Exception('No person given');
}
//Validate list
foreach ($personList as $person) {
if (!$person instanceof Person) {
throw new Exception('This is not a person');
}
}
//Keep persons
$this->personList = $personList;
}
Also note, if we want to have multiple Objects in our class, we can make an ContainerObject:
class ContainerObject {
public $person;
public $moreInfo;
public $manyMoreInfo;
}
class Person {
public function sayHello() {};
}
class Car {
private $containerObj;
function __construct(ContainerObject $obj) {
$this->containerObj= $obj;
}
function foo() {
$this->containerObj->person->sayHello();
}
}
I am quite new to the Data Mapper pattern. I guess I am missing something, because, for me, it fails for everything more complicated than the most basic examples.
Let's say I have a simple website composed of pages:
class Page {
private $id = null;
private $parent = null;
private $title = null;
private $body = null;
public function setId($id) { $this->id = (int) $id; }
public function setParent(Page $parent) { $this->parent = $parent; }
public function setTitle($title) { $this->title = $title; }
public function setBody($body) { $this->body = $body; }
public function getId() { return $this->id; }
public function getParent() { return $this->parent; }
public function getTitle() { return $this->title; }
public function getBody() { return $this->body; }
}
Now I want to instantiate Page 7 that is deep in the tree:
Page 1
Page 2
Page 3
Page 4
Page 5
Page 6
Page 7
Using the Data Mapper pattern I would use the following mapper class:
class PageMapper {
public function fetch($id) {
//...
$data = $db->fetchRow("SELECT * FROM `pages` WHERE `id` = ?", $id);
$page = new Page();
$page->setId($data['id']);
$page->setTitle($data['title']);
$page->setBody($data['body']);
if ($data['parent_id'] !== null) {
$page->setParent(
$this->fetch($data['parent_id']);
);
}
}
public function save(Page $page) {
//...
}
}
The problem is obvious: I must instantiate all parents all the way up to the root Page.
Now imagine a Page would need to know about its children. That would mean, if I would want to instantiate the root Page, I would have to load the entire tree.
A solution could be that the parent/children would be loaded only on demand, but that would mean that the domain objects would have to know about the mapper in order to pull data from it... and the separation would be gone.
The domain models (instances of the Page class) should know nothing about the Data Mapper layer (separation), but still be able to perform those tasks (retrieving parent/children).
Is separation of concerns achievable in these conditions? If yes, how?
Look at Doctrine, a orm framework which implements the data mapper pattern:
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html#one-to-many-self-referencing
The problem with your approach is not the instantiation of the entire tree (children), but
the amount of queries it would use.
Look at the doctrine approach, there you would have a children property, which is loaded at once with one query.
I would also never recommend to implement a self-made solution for an orm.
A possible solution would be also to use a Closure:
class Page {
//...
public function setParents(Closure $pages)
{
$this->parents = $pages;
}
}
class PagesMapper {
public function fetch() {
//fetch the page ...
$parents = function($parent) use($id, $db) {
parents = $db->query(/* select parent... */);
$pages = array();
foreach($parents as $parent) {
$page = new Page();
$page->id = $parent->id;
//...
$pages[] = $page;
}
return $pages;
};
$page->setParents($parents);
return $page;
}
}
So the page domain would have no knowledge of the persistance layer.
I have been reading Rafactoring by Martin Fowler and in the beginning of the book he uses an example application (written in Java), which I am trying to convert over to PHP for training purposes. (I have trimmed the code down to make this question, but it works if the variables are public.)
The trouble is to create a statement I need access to a value (see switch) using method getCode() of Movie class since $code is private. (Of course if all of the variables were public the code below would work, but I want to keep them private.)
Can someone please shed some light on how I would access the private variable calling the getCode() method of Movie from the switch in statement(). (Or if there is a better way to do it, let me know.)
class Movie {
private $title;
private $code;
public function __construct($title, $code) {
$this->title = $title;
$this->code = $code;
}
public function getCode() {
return $this->code;
}
public function getTitle() {
return $this->title;
}
}
class Rental {
private $movie; // will carry a Movie object
private $days;
public function __construct(Movie $movie, $days) {
$this->movie = $movie;
$this->days = $days;
}
public function getMovie() {
return $this->movie;
}
}
class Customer {
private $name;
private $rentals; // will be a collection of Rental Objects
public function __construct($name) {
$this->name = $name;
}
public function addRental(Rental $rental) {
$this->rentals[] = $rental;
}
public function statement() {
$thisAmount = 0;
foreach ($this->rentals as $each) {
// what is the better way to call this value??????
switch ($each->movie->code) {
case 1:
$thisAmount+= ($each->days - 2) * 1.5;
break;
case 2:
$thisAmount += $each->days * 3;
break;
case 3:
$thisAmount += 1.5;
break;
}
// show figures for this rental
$result = "\t" . $each->movie->title . "\t" . $thisAmount . "\n";
}
return $result;
}
}
// pick a movie
$movie = new Movie('Star Wars', 0);
// now rent it
$rental = new Rental($movie, '2');
// now get statement
$customer = new Customer('Joe');
$customer->addRental($rental);
echo $customer->statement();
You are iterating over a collection of movie in your foreach. So you can do it this way:
foreach($this->rentals as $rental) {
switch($rental->getMovie()->getCode()) {
Of course, you can leave your variable named each. I just find $movie more readable and understandable in this context.
replace your line with just:
$each->getMovie->getCode()
I need to implement the following pattern in php:
class EventSubscriber
{
private $userCode;
public function __construct(&$userCode) { $this->userCode = &$userCode; }
public function Subscribe($eventHandler) { $userCode[] = $eventHandler; }
}
class Event
{
private $subscriber;
private $userCode = array();
public function __construct()
{
$this->subscriber = new Subscriber($this->userCode)
}
public function Subscriber() { return $this->subscriber; }
public function Fire()
{
foreach ($this->userCode as $eventHandler)
{
/* Here i need to execute $eventHandler */
}
}
}
class Button
{
private $eventClick;
public function __construct() { $this->eventClick = new Event(); }
public function EventClick() { return $this->eventClick->Subscriber(); }
public function Render()
{
if (/* Button was clicked */) $this->eventClick->Fire();
return '<input type="button" />';
}
}
class Page
{
private $button;
// THIS IS PRIVATE CLASS MEMBER !!!
private function ButtonClickedHandler($sender, $eventArgs)
{
echo "button was clicked";
}
public function __construct()
{
$this->button = new Button();
$this->button->EventClick()->Subscribe(array($this, 'ButtonClickedHandler'));
}
...
}
what is the correct way to do so.
P.S.
I was using call_user_func for that purpose and believe it or not it was able to call private class members, but after few weeks of development i've found that it stopped working. Was it a bug in my code or was it some something else that made me think that 'call_user_func' is able call private class functions, I don't know, but now I'm looking for a simple, fast and elegant method of safely calling one's private class member from other class. I'm looking to closures right now, but have problems with '$this' inside closure...
Callbacks in PHP aren't like callbacks in most other languages. Typical languages represent callbacks as pointers, whereas PHP represents them as strings. There's no "magic" between the string or array() syntax and the call. call_user_func(array($obj, 'str')) is syntactically the same as $obj->str(). If str is private, the call will fail.
You should simply make your event handler public. This has valid semantic meaning, i.e., "intended to be called from outside my class."
This implementation choice has other interesting side effects, for example:
class Food {
static function getCallback() {
return 'self::func';
}
static function func() {}
static function go() {
call_user_func(self::getCallback()); // Calls the intended function
}
}
class Barf {
static function go() {
call_user_func(Food::getCallback()); // 'self' is interpreted as 'Barf', so:
} // Error -- no function 'func' in 'Barf'
}
Anyway, if someone's interested, I've found the only possible solution via ReflectionMethod. Using this method with Php 5.3.2 gives performance penalty and is 2.3 times slower than calling class member directly, and only 1.3 times slower than call_user_func method. So in my case it is absolutely acceptable. Here's the code if someone interested:
class EventArgs {
}
class EventEraser {
private $eventIndex;
private $eventErased;
private $eventHandlers;
public function __construct($eventIndex, array &$eventHandlers) {
$this->eventIndex = $eventIndex;
$this->eventHandlers = &$eventHandlers;
}
public function RemoveEventHandler() {
if (!$this->eventErased) {
unset($this->eventHandlers[$this->eventIndex]);
$this->eventErased = true;
}
}
}
class EventSubscriber {
private $eventIndex;
private $eventHandlers;
public function __construct(array &$eventHandlers) {
$this->eventIndex = 0;
$this->eventHandlers = &$eventHandlers;
}
public function AddEventHandler(EventHandler $eventHandler) {
$this->eventHandlers[$this->eventIndex++] = $eventHandler;
}
public function AddRemovableEventHandler(EventHandler $eventHandler) {
$this->eventHandlers[$this->eventIndex] = $eventHandler;
$result = new EventEraser($this->eventIndex++, $this->eventHandlers);
return $result;
}
}
class EventHandler {
private $owner;
private $method;
public function __construct($owner, $methodName) {
$this->owner = $owner;
$this->method = new \ReflectionMethod($owner, $methodName);
$this->method->setAccessible(true);
}
public function Invoke($sender, $eventArgs) {
$this->method->invoke($this->owner, $sender, $eventArgs);
}
}
class Event {
private $unlocked = true;
private $eventReceiver;
private $eventHandlers;
private $recursionAllowed = true;
public function __construct() {
$this->eventHandlers = array();
}
public function GetUnlocked() {
return $this->unlocked;
}
public function SetUnlocked($value) {
$this->unlocked = $value;
}
public function FireEventHandlers($sender, $eventArgs) {
if ($this->unlocked) {
//защита от рекурсии
if ($this->recursionAllowed) {
$this->recursionAllowed = false;
foreach ($this->eventHandlers as $eventHandler) {
$eventHandler->Invoke($sender, $eventArgs);
}
$this->recursionAllowed = true;
}
}
}
public function Subscriber() {
if ($this->eventReceiver == null) {
$this->eventReceiver = new EventSubscriber($this->eventHandlers);
}
return $this->eventReceiver;
}
}
As time passes, there are new ways of achieving this.
Currently PSR-14 is drafted to handle this use case.
So you might find any of these interesting:
https://packagist.org/?query=psr-14