Skip to content

Instantly share code, notes, and snippets.

@Kcko
Last active March 21, 2020 14:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Kcko/ee36cb59918c61b08e61805798fc55fd to your computer and use it in GitHub Desktop.
Save Kcko/ee36cb59918c61b08e61805798fc55fd to your computer and use it in GitHub Desktop.
<?php
interface Entity
{
public function accept(Visitor $visitor): string;
}
class Company implements Entity
{
private $name;
/**
* @var Department[]
*/
private $departments;
public function __construct(string $name, array $departments)
{
$this->name = $name;
$this->departments = $departments;
}
public function getName(): string
{
return $this->name;
}
public function getDepartments(): array
{
return $this->departments;
}
// ...
public function accept(Visitor $visitor): string
{
// See, the Company component must call the visitCompany method. The
// same principle applies to all components.
return $visitor->visitCompany($this);
}
}
class Department implements Entity
{
private $name;
/**
* @var Employee[]
*/
private $employees;
public function __construct(string $name, array $employees)
{
$this->name = $name;
$this->employees = $employees;
}
public function getName(): string
{
return $this->name;
}
public function getEmployees(): array
{
return $this->employees;
}
public function getCost(): int
{
$cost = 0;
foreach ($this->employees as $employee) {
$cost += $employee->getSalary();
}
return $cost;
}
// ...
public function accept(Visitor $visitor): string
{
return $visitor->visitDepartment($this);
}
}
class Employee implements Entity
{
private $name;
private $position;
private $salary;
public function __construct(string $name, string $position, int $salary)
{
$this->name = $name;
$this->position = $position;
$this->salary = $salary;
}
public function getName(): string
{
return $this->name;
}
public function getPosition(): string
{
return $this->position;
}
public function getSalary(): int
{
return $this->salary;
}
// ...
public function accept(Visitor $visitor): string
{
return $visitor->visitEmployee($this);
}
}
interface Visitor
{
public function visitCompany(Company $company): string;
public function visitDepartment(Department $department): string;
public function visitEmployee(Employee $employee): string;
}
class SalaryReport implements Visitor
{
public function visitCompany(Company $company): string
{
$output = "";
$total = 0;
foreach ($company->getDepartments() as $department) {
$total += $department->getCost();
$output .= "\n--" . $this->visitDepartment($department);
}
$output = $company->getName() .
" (" . money_format("%i", $total) . ")\n" . $output;
return $output;
}
public function visitDepartment(Department $department): string
{
$output = "";
foreach ($department->getEmployees() as $employee) {
$output .= " " . $this->visitEmployee($employee);
}
$output = $department->getName() .
" (" . money_format("%i", $department->getCost()) . ")\n\n" .
$output;
return $output;
}
public function visitEmployee(Employee $employee): string
{
return money_format("%#6n", $employee->getSalary()) .
" " . $employee->getName() .
" (" . $employee->getPosition() . ")\n";
}
}
// USAGE
$mobileDev = new Department("Mobile Development", [
new Employee("Albert Falmore", "designer", 100000),
new Employee("Ali Halabay", "programmer", 100000),
new Employee("Sarah Konor", "programmer", 90000),
new Employee("Monica Ronaldino", "QA engineer", 31000),
new Employee("James Smith", "QA engineer", 30000),
]);
$techSupport = new Department("Tech Support", [
new Employee("Larry Ulbrecht", "supervisor", 70000),
new Employee("Elton Pale", "operator", 30000),
new Employee("Rajeet Kumar", "operator", 30000),
new Employee("John Burnovsky", "operator", 34000),
new Employee("Sergey Korolev", "operator", 35000),
]);
$company = new Company("SuperStarDevelopment", [$mobileDev, $techSupport]);
setlocale(LC_MONETARY, 'en_US');
$report = new SalaryReport;
echo "Client: I can print a report for a whole company:\n\n";
echo $company->accept($report);
echo "\nClient: ...or just for a single department:\n\n";
echo $techSupport->accept($report);
/*
Client: I can print a report for a whole company:
SuperStarDevelopment (USD550,000.00)
--Mobile Development (USD351,000.00)
$100,000.00 Albert Falmore (designer)
$100,000.00 Ali Halabay (programmer)
$ 90,000.00 Sarah Konor (programmer)
$ 31,000.00 Monica Ronaldino (QA engineer)
$ 30,000.00 James Smith (QA engineer)
--Tech Support (USD199,000.00)
$ 70,000.00 Larry Ulbrecht (supervisor)
$ 30,000.00 Elton Pale (operator)
$ 30,000.00 Rajeet Kumar (operator)
$ 34,000.00 John Burnovsky (operator)
$ 35,000.00 Sergey Korolev (operator)
Client: ...or just for a single department:
Tech Support (USD199,000.00)
$ 70,000.00 Larry Ulbrecht (supervisor)
$ 30,000.00 Elton Pale (operator)
$ 30,000.00 Rajeet Kumar (operator)
$ 34,000.00 John Burnovsky (operator)
$ 35,000.00 Sergey Korolev (operator)
*/
<?php
interface RoleVisitor
{
public function visitUser(User $role);
public function visitGroup(Group $role);
}
interface Role
{
public function accept(RoleVisitor $visitor);
}
class RecordingVisitor implements RoleVisitor
{
/**
* @var Role[]
*/
private array $visited = [];
public function visitGroup(Group $role)
{
$this->visited[] = $role;
}
public function visitUser(User $role)
{
$this->visited[] = $role;
}
/**
* @return Role[]
*/
public function getVisited(): array
{
return $this->visited;
}
}
class User implements Role
{
private string $name;
public function __construct(string $name)
{
$this->name = $name;
}
public function getName(): string
{
return sprintf('User %s', $this->name);
}
public function accept(RoleVisitor $visitor)
{
$visitor->visitUser($this);
}
}
class Group implements Role
{
private string $name;
public function __construct(string $name)
{
$this->name = $name;
}
public function getName(): string
{
return sprintf('Group: %s', $this->name);
}
public function accept(RoleVisitor $visitor)
{
$visitor->visitGroup($this);
}
}
<?php
interface Visitable
{
public function accept(Visitor $visitor);
}
interface Visitor
{
public function visitUniversity(University $university): array;
public function visitStudent(Student $student): string;
}
class University implements Visitable
{
private $name;
private $students;
public function __construct(string $name, array $students)
{
$this->name = $name;
$this->students = $students;
}
public function getName(): string
{
return $this->name;
}
public function getStudents(): array
{
return $this->students;
}
public function accept(Visitor $visitor): array
{
return $visitor->visitUniversity($this);
}
}
class Student implements Visitable
{
private $name;
private $sickLeaves = [];
public function __construct(string $name)
{
$this->name = $name;
}
public function addSickLeave(\DateTime $start, \DateTime $end) : void
{
$this->sickLeaves[] = new SickLeave($start, $end);
}
public function getName(): string
{
return $this->name;
}
public function getSickLeaves(): array
{
return $this->sickLeaves;
}
public function accept(Visitor $visitor): string
{
return $visitor->visitStudent($this);
}
}
class SickLeaveReport implements Visitor
{
public function visitUniversity(University $university): array
{
$msgs = [];
$msgs[] = "Generating sick leave report for: \"{$university->getName()}\"";
foreach ($university->getStudents() as $student) {
$msgs[] = $this->visitStudent($student);
}
return $msgs;
}
public function visitStudent(Student $student): string
{
$daysMissed = 0;
foreach ($student->getSickLeaves() as $sickLeave) {
$daysMissed += $sickLeave->getStart()->diff($sickLeave->getEnd())->days + 1;
}
return "Student: {$student->getName()} missed {$daysMissed} days";
}
}
class SickLeave
{
private $start;
private $end;
public function __construct(\DateTime $start, \DateTime $end)
{
$this->start = $start;
$this->end = $end;
}
public function getStart(): \DateTime
{
return $this->start;
}
public function getEnd(): \DateTime
{
return $this->end;
}
}
// USAGE
$john = new Student("John");
$john->addSickLeave(new \DateTime("2019-10-01"), new \DateTime("2019-10-21"));
$john->addSickLeave(new \DateTime("2019-11-02"), new \DateTime("2019-11-10"));
$jan = new Student("Jan");
$jan->addSickLeave(new \DateTime("2019-11-01"), new \DateTime("2019-11-15"));
$ann = new Student("Ann");
$university = new University("Visitor University", [$john, $jan, $ann]);
$results = $university->accept(new SickLeaveReport());
function show($data)
{
foreach ($data as $datum) {
echo $datum.PHP_EOL;
}
}
show($results);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment