官术网_书友最值得收藏!

Liskov substitution principle

The Liskov substitution principle talks about inheritance. It specifies how we should design our classes so that client dependencies can be replaced by subclasses without the client seeing the difference, as per the definition found on Wikipedia:

"objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program"

While there might be some specific functionality added to the subclass, it has to conform to the same behavior as its base class. Otherwise the Liskov principle is violated.

When it comes to PHP and sub-classing, we have to look beyond simple concrete classes and differentiate: concrete class, abstract class, and interface. Each of the three can be put in the context of a base class, while everything extending or implementing it can be looked at as a derived class.

The following is an example of LSP violation, where the derived class does not have an implementation for all methods:

interface User {
    public function getEmail();
    public function getName();
    public function getAge();
}

class Employee implements User {
    public function getEmail() {
        // Implementation...
    }

    public function getAge() {
        // Implementation...
    }
}

Here we see an employee class which does not implement the getName method enforced by the interface. We could have easily used an abstract class instead of the interface and abstract method type for the getName method, the effect would have been the same. Luckily, the PHP would throw an error in this case, warning us that we haven't really implemented the interface fully.

The following is an example of Liskov principle violation, where different derived classes return things of different types:

class UsersCollection implements \Iterator {
    // Implementation...
}

interface UserList {
    public function getUsers();
}

class Emloyees implements UserList {
    public function getUsers() {
        $users = new UsersCollection();
        //...
        return $users;
    }
}

class Directors implements UserList {
    public function getUsers() {
        $users = array();
        //...
        return $users;
    }
}

Here we see a simple example of an edge case. Calling getUsers on both derived classes will return a result we can loop through. However, PHP developers tend to use the count method often on array structures, and using it on Employees instances the getUsers result will not work. This is because the Employees class returns UsersCollection which implements Iterator, not the actual array structure. Since UsersCollection does not implement Countable, we cannot use count on it, which leads to potential bugs down the line.

We can further spot LSP violations in cases where the derived class behaves less permissively with regard to method arguments. These can usually be spotted by use of the instance of type operator, as shown in the following example:

interface LoggerProcessor {
    public function log(LoggerInterface $logger);
}

class XmlLogger implements LoggerInterface {
    // Implementation...
}

class JsonLogger implements LoggerInterface {
    // Implementation...
}

class FileLogger implements LoggerInterface {
    // Implementation...
}

class Processor implements LoggerProcessor {
    public function log(LoggerInterface $logger) {
        if ($logger instanceof XmlLogger) {
            throw new \Exception('This processor does not work with XmlLogger');
        } else {
            // Implementation...
        }
    }
}

Here, the derived class Processor puts restrictions on method arguments, while it should accept everything conforming to the LoggerInterface. By being less permissive, it alters the behavior implied by the base class, in this case LoggerInterface.

The outlined examples are merely a fragment of what constitutes a violation of LSP. To satisfy the principle, we need to make sure that derived classes do not, in any way, alter the behavior imposed by the base class.

主站蜘蛛池模板: 金坛市| 弥渡县| 龙海市| 辰溪县| 衡水市| 平江县| 南部县| 明水县| 慈溪市| 蒙城县| 茶陵县| 西吉县| 河间市| 九台市| 瑞昌市| 崇礼县| 化州市| 徐闻县| 通化市| 花莲县| 玉树县| 油尖旺区| 皮山县| 塔城市| 伊吾县| 洱源县| 明溪县| 峨山| 宁乡县| 门头沟区| 弥渡县| 盐津县| 兖州市| 和平区| 茌平县| 宽甸| 菏泽市| 双流县| 辽阳市| 开江县| 鞍山市|