Основы ООП. Наследование

Краткое содержание лекции

Теоретические задания | Практические задания

С наследованием мы частично столкнулись при изучении синтаксиса создания классов. При условии, что такое наследование вам известно, мы начнем сразу с практики.

Хороший код сразу написать трудно, также трудно учесть всевозможные ситуации, пожалуй, даже неправильно учитывать того, чего не существует. Пишите код для того, что вам действительно нужно.

Если у вас есть группа объектов, которые имеют общие свойства, методы, возможно, вам стоит прибегнуть к наследованию, что сократит дублирование кода.

Я долго думал над примером, не хотелось что-то мифического. На своей работе я имею дело с печатным производством. На печатной фабрике большая разновидность машин: различные виды печатных станков (printing press, uv press), ламинатор (laminating machine), ксерокс (copier) и так далее.

(AbstractMachine.php) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
<?php
/**
 * @author Dmitry Petrov <dmitry.petrov@opensoftdev.ru>
 */
abstract class AbstractMachine
{
    const PRINTING_PRESS = 'printing press';
    const UV_PRESS = 'uv press';
    const LAMINATING_MACHINE = 'laminating machine';
    const COPIER = 'copier';

    /**
     * @var string
     */
    private $name;

    /**
     * @var string
     */
    private $vendorName;

    /**
     * @var integer
     */
    private $year;

    /**
     * @return string
     */
    abstract public function getType();

    /**
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * @param string $name
     */
    public function setName($name)
    {
        $this->name = $name;
    }

    /**
     * @return string
     */
    public function getVendorName()
    {
        return $this->vendorName;
    }

    /**
     * @param string $vendorName
     */
    public function setVendorName($vendorName)
    {
        $this->vendorName = $vendorName;
    }

    /**
     * @return integer
     */
    public function getYear()
    {
        return $this->name;
    }

    /**
     * @param integer $year
     */
    public function setYear($year)
    {
        $this->year = $year;
    }
}
(PrintingPress.php) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<?php
/**
 * @author Dmitry Petrov <dmitry.petrov@opensoftdev.ru>
 */
class PrintingPress extends AbstractMachine
{
    /**
     * @var integer
     */
    private $speed;

    /**
     * @return boolean
     */
    public function isDuplexPrinting()
    {
        return true;
    }

    /**
     * @return boolean
     */
    public function isQuickPrint()
    {
        return true;
    }

    /**
     * @return integer
     */
    public function getSpeed()
    {
        return true;
    }

    /**
     * @return string
     */
    public function getType()
    {
        return self::PRINTING_PRESS;
    }
}
(UvPress.php) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
/**
 * @author Dmitry Petrov <dmitry.petrov@opensoftdev.ru>
 */
class UvPress extends AbstractMachine
{
    /**
     * @return string
     */
    public function getType()
    {
        return self::UV_PRESS;
    }
}
(LaminatingMachine.php) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
/**
 * @author Dmitry Petrov <dmitry.petrov@opensoftdev.ru>
 */
class LaminatingMachine extends AbstractMachine
{
    /**
     * @return string
     */
    public function getType()
    {
        return self::LAMINATING_MACHINE;
    }
}
(Copier.php) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?php
/**
 * @author Dmitry Petrov <dmitry.petrov@opensoftdev.ru>
 */
class Copier extends AbstractMachine
{
    /**
     * @return string
     */
    public function getType()
    {
        return self::COPIER;
    }

    /**
     * @return integer
     */
    public function getPrintingSpeed()
    {
        return 100;
    }

    /**
     * @return integer
     */
    public function getScanSpeed()
    {
        return 10;
    }
}

Мы вынесли общие части связанных объектов в абстрактный класс, а специфические свойства реализовали в классах потомках. Какие потенциальные ошибки мы при этом допустили? Исправьте код.

Еще часто прибегают к наследованию, когда у вас есть какой-то класс (возможно из другой библиотеки), и вам понадобилось расширить его функционал.

(User.php) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?php
/**
 * @author Dmitry Petrov <dmitry.petrov@opensoftdev.ru>
 */
class User
{
    /**
     * @var integer
     */
    private $id;

    /**
     * @var string
     */
    private $username;

    /**
     * @var string
     */
    private $password;

    /**
     * @return string
     */
    public function getUsername()
    {
        return $this->username;
    }

    //another methods
}
(AdminUser.php) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
/**
 * @author Dmitry Petrov <dmitry.petrov@opensoftdev.ru>
 */
class AdminUser extends User
{
    /**
     * @param string $username
     */
    public function removeUser($username)
    {
        //code
    }

    /**
     * @param string $username
     */
    public function banUser($username)
    {
        //code
    }
}

Наследование - это не золотая пуля.

Практически все классы, работающие с базой данных, имеют поле id. Но это не означает, что нужно создать базовый класс и наследовать от него все остальные.

(BasicClass.php) download
1
2
3
4
5
6
7
8
9
10
11
<?php
/**
 * @author Dmitry Petrov <dmitry.petrov@opensoftdev.ru>
 */
abstract class BasicClass
{
    /**
     * @var integer
     */
    protected $id;
}
(User.php) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?php
/**
 * @author Dmitry Petrov <dmitry.petrov@opensoftdev.ru>
 */
class User extends BasicClass
{
    /**
     * @var string
     */
    private $username;

    /**
     * @var string
     */
    private $password;

    /**
     * @return string
     */
    public function getUsername()
    {
        return $this->username;
    }

    //another methods
}
(News.php) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
/**
 * @author Dmitry Petrov <dmitry.petrov@opensoftdev.ru>
 */
class News extends BasicClass
{
    /**
     * @var string
     */
    private $author;

    /**
     * @var string
     */
    private $text;

    //another methods
}

Не стоит также смешивать с полиморфизмом. Машина и велосипед имеют колеса, но наличие колес в данном случае - это не некая общность, которую обязательно следует вынести в общий класс.

(Wheel.php) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
/**
 * @author Dmitry Petrov <dmitry.petrov@opensoftdev.ru>
 */
class Wheel
{
    /**
     * @return boolean
     */
    public function hasWheels()
    {
        //code
    }

    /**
     * @return integer
     */
    public function countWheel()
    {
        //code
    }
}
(Auto.php) download
1
2
3
4
5
<?php
class Auto extends Wheel
{

}
(Bicycle.php) download
1
2
3
4
5
<?php
class Bicycle extends Wheel
{
    //code
}