Why do so many have problems with this principle? If we take not "abstruse", but a simpler definition, then it sounds like this:
The inheriting class should complement, not override, the behavior of the base class.
It sounds clear and quite logical, we disagree. but damn how to achieve this? For some reason, many people simply skip information about preconditions and postconditions , which just perfectly explain what needs to be done.
In this article, we will NOT consider general examples of this principle, about which there are already many materials (example with a square and a rectangle or thermostat controls ). Here we will dwell a little more on such concepts as "Preconditions" , "Postconditions" , consider what covariance, contravariance and invariance are , as well as what "historical constraints" or "rule of history" are.
Preconditions cannot be strengthened in a subclass
οΈIn other words, child classes should not create more preconditions than defined in the base class in order to perform some business behavior. Here's an example:
<?php
class Customer
{
protected float $account = 0;
public function putMoneyIntoAccount(int|float $sum): void
{
if ($sum < 1) {
throw new Exception(' 1$');
}
$this->account += $sum;
}
}
class MicroCustomer extends Customer
{
public function putMoneyIntoAccount(int|float $sum): void
{
if ($sum < 1) {
throw new Exception(' 1$');
}
//
if ($sum > 100) {
throw new Exception(' 100$');
}
$this->account += $sum;
}
}
β . !
«», , .
, , .
, , Bar->process()
, .
<?php
class Foo
{
public function process(int|float $value)
{
// some code
}
}
class Bar extends Foo
{
public function process(int|float|string $value)
{
// some code
}
}
, VIPCustomer
putMoneyIntoAccount
( ) Money
, ( Dollars
).
<?php
class Money {}
class Dollars extends Money {}
class Customer
{
protected Money $account;
public function putMoneyIntoAccount(Dollars $sum): void
{
$this->account = $sum;
}
}
class VIPCustomer extends Customer
{
public function putMoneyIntoAccount(Money $sum): void
{
$this->account = $sum;
}
}
, , .
βοΈ , . .
<?php
class Customer
{
protected Dollars $account;
public function chargeMoney(Dollars $sum): float
{
$result = $this->account - $sum->getAmount();
if ($result < 0) { //
throw new Exception();
}
return $result;
}
}
class VIPCustomer extends Customer
{
public function chargeMoney(Dollars $sum): float
{
$result = $this->account - $sum->getAmount();
if ($sum < 1000) { //
$result -= 5;
}
//
return $result;
}
}
β , . !
- «», (?!), .
. render()
, JpgImage
, Image
, Renderer
.
<?php
class Image {}
class JpgImage extends Image {}
class Renderer
{
public function render(): Image
{
}
}
class PhotoRenderer extends Renderer
{
public function render(): JpgImage
{
}
}
βοΈ . . :)
.
- .
β , . , .
.
<?php
class Wallet
{
protected float $amount;
//
}
(Β« Β»):
.
, . , .
<?php
class Deposit
{
protected float $account = 0;
public function __construct(float $sum)
{
if ($sum < 0) {
throw new Exception(' ');
}
$this->account += $sum;
}
}
class VipDeposit extends Deposit
{
public function getMoney(float $sum)
{
$this->account -= $sum;
}
}
β Deposit
. VipDeposit
, account
, Deposit
. .
.
, , , , .
It is worth mentioning to strive to get rid of pre / post conditions. Ideally, they should be defined as input / output parameters of the method (for example, by passing ready-made value objects to the signature and returning a specific valid object to the output).
Hope it was helpful.