• 技术文章 >后端开发 >PHP问题

    面向对象的三大特性和五大原则是什么

    步履不停步履不停2019-06-21 16:45:39原创1542

    面向对象的三大特性

    封装,继承,多态

    什么是封装?

    把客观的事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的类进行信息的隐藏。简单的说就是:封装使对象的设计者与对象的使用者分开,使用者只要知道对象可以做什么就可以了,不需要知道具体是怎么实现的。封装可以有助于提高类和系统的安全性。

    什么是继承?

    继承指的是建立一个新的派生类,从一个或多个先前定义的类中继承数据和函数,可以重新定义或加进新数据和函数,从而建立了类的层次或等级。

    什么是多态?

    多态性指的是: 同一操作作用与不同类的实例,将产生不同的执行结果,即不同类的对象收到相同的消息时,将得到不同的结果。

    代码示例

    class eat
    {
        public function breakfast()
        {
            echo "吃早饭!";
        }
    }
    
    class typist
    {
        public function type()
        {
            echo "打字!";
        }
    
    }
    
    function printWorking($obj)
    {
        if ($obj instanceof eat) {
            echo $obj->breakfast();
        } elseif ($obj instanceof typist) {
            echo $obj->type();
        } else {
            echo "error";
        }
    }
    
    printWorking(new eat());
    echo PHP_EOL;
    printWorking(new typist());

    输出:

    吃早饭! 打字!

    面向对象的五大原则

    单一职责原则,开放封闭原则,里氏替换原则,依赖倒置原则,接口隔离原则

    什么是单一职责原则?

    简单的来说就是: 一个类只做一件事情,不要为类实现过多的功能点,避免相同的职责分散到不同的类中。如果一个类的职责过多可能引起变化的原因也就越多,使代码更加的耦合。如果杂交不清的职责将使得代码难以维护,牵一发而动全身。

    例如:工厂模式,工厂模式之所以被称为工厂模式是因为它负责 “生产” 对象。

    代码示例

    工厂模式的好处:多个地方new了一个对象,当此类名发生改变就不需要一个个去修改,只需要修改一处地方。

    <?php
    class MyObject
    {
        public function __construct()
        {
            echo "test code";
        }
    }
    
    //工厂类
    class MyFactory
    {
        public static function factory()
        {
            return new MyObject();
       }
    }
    
    $instance = MyFactory::factory();//test code

    什么是开放封闭原则?

    开放封闭原则: 一个类是可扩展的,而不可修改的。也就是说,对扩展开放,对修改封闭。

    1.对扩展开放,意味着 有新的需求或变化时,可以对现有的代码进行扩展,以适应新的情况。

    2.对修改封闭,在对模块功能进行扩展时,不应该影响已有的程序模块。

    实现开放封闭的原则的重点:抽象编程,而不是具体编程,因为抽象相对稳定,让类依赖与固定的抽象类和接口,所以修改就是封闭的。而面向对象的继承和多态机制,又可以继承抽象类或者实现接口,听过重写其方法来改变固有的行为,实现方法新的拓展,所以就是开放。

    例如:装饰器模式(Decorator),可以动态地添加修改类的功能。一个类提供了一项功能,如果要在修改并添加额外的功能,传统的编程模式,需要写一个子类去继承它,并重新实现类的方法,使用装饰器模式,仅需在运行时添加一个装饰器对象即可实现,可以实现最大的灵活性。

    <?php
    
    /**
     * 输出一个字符串
     * 装饰器动态添加功能
     * Class EchoText
     */
    class EchoText
    {
        protected $decorator = [];
    
        public function Index()
        {
            //调用装饰器前置操作
            $this->beforeEcho();
            echo "你好,我是装饰器。";
            //调用装饰器后置操作
            $this->afterEcho();
        }
    
        //增加装饰器
        public function addDecorator(Decorator $decorator)
        {
            $this->decorator[] = $decorator;
        }
    
        //执行装饰器前置操作 先进先出原则
        protected function beforeEcho()
        {
            foreach ($this->decorator as $decorator)
                $decorator->before();
        }
    
        //执行装饰器后置操作 先进后出原则
        protected function afterEcho()
        {
            $tmp = array_reverse($this->decorator);
            foreach ($tmp as $decorator)
                $decorator->after();
        }
    }
    
    /**
     * 装饰器接口
     * Class Decorator
     */
    interface Decorator
    {
        public function before();
    
        public function after();
    }
    
    /**
     * 颜色装饰器实现
     * Class ColorDecorator
     */
    class ColorDecorator implements Decorator
    {
        protected $color;
    
        public function __construct($color)
        {
            $this->color = $color;
        }
    
        public function before()
        {
            echo "<dis style='color: {$this->color}'>";
        }
    
        public function after()
        {
            echo "</div>";
        }
    }
    
    /**
     * 字体大小装饰器实现
     * Class SizeDecorator
     */
    class SizeDecorator implements Decorator
    {
        protected $size;
    
        public function __construct($size)
        {
            $this->size = $size;
        }
    
        public function before()
        {
            echo "<dis style='font-size: {$this->size}px'>**";
        }
    
        public function after()
        {
            echo "</div>";
        }
    }
    
    //实例化输出类
    $echo = new EchoText();
    //增加装饰器
    $echo->addDecorator(new ColorDecorator('red'));
    //增加装饰器
    $echo->addDecorator(new SizeDecorator('22'));
    //输出
    $echo->Index();

    什么是里氏替换原则?

    由于面向对象编程技术中的继承在具体编程中过于简单,在许多系统的设计和编程实现中,我们并没有认真地、理性地思考应用系统中各个类之间的继承好关系是否合适,派生类是否能正确地对基类中的某些方法进行重写的问题。因此经常出现滥用继承或者错误的继承现象,给系统后期的维护带来了不少麻烦。

    核心思想:子类必须能够替换其父类。这一思想体现为对继承机制的约束规范,只有子类能够替换父类时才能保证系统在运行期内识别子类,这是保证继承复用的基础。

    <?php
    //例子1
    class Bird{
        protect function fly(){
    
        }
    }
    //翠鸟
    class KingFisher extends Bird{
    
    }
    
    //鸵鸟
    class Ostrich extends Bird{
        //鸵鸟不会飞啊
    }
    
    //例子2
    
    class A{
        protect function add($a, $b){
            return $a + $b;
        }
    }
    
    //重载
    class B extends A{
    
        protected function add($a, $b){
            return $a + $b + 100;
        }
    }

    里氏替换原则是对类继承的一种约束。对里氏替换原则有两种理解:

    1.不能随便去继承不合适的,有多余方法或者属性的类(例子1)。

    2.子类可以扩展父类的功能,但不能改变父类原有的功能(例子2)。

    里氏替换原则包含一下几个隐藏含义:

    1.子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。

    2.子类中可以增加自己特有的方法。

    3.当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。

    4.当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。

    什么是依赖倒置原则?

    简单的来说就是:一个类,不应该强制依赖另一个类,每个类对于另外一个类都是可替换的。

    具体概念:

    1.上层模块不应该依赖于下层模块,它们共同依赖于一个抽象(父类不能依赖子类,它们都要依赖抽象类)。

    2.抽象不能依赖于具体,具体应该依赖于抽象。

    为什么要依赖接口?因为接口体现对问题的抽象,同时由于抽象一般是相对稳定的或者是相对变化不频繁的,而具体是易变的。因此,依赖抽象是实现代码扩展和运行期内绑定(多态)的基础:只要实现了该抽象类的子类,都可以被类的使用者使用。

    <?php
    
    interface employee
    {
        public function working();
    }
    
    class teacher implements employee//具体应该依赖与抽象
    {
        public function working(){
            echo 'teaching...';
        }
    }
    
    class coder implements employee
    {
        public function working(){
            echo 'coding...';
        }
    }
    
    class workA//例子1
    {
        public function work(){
            $teacher = new teacher;
            $teacher->working();
        }
    }
    
    class workB//例子2
    {
        private $e;
        public function set(employee $e){
            $this->e = $e;
        }
    
        public function work(){
            $this->e->working();
        }
    }
    
    $worka = new workA;//workA 依赖于 teacher 类 不符合依赖倒置原则
    $worka->work();
    $workb = new workB;//workB 不依赖与某个类 既可以注入 teacher 也可以 注入 coder
    $workb->set(new teacher());
    $workb->work();

    在workA(例子1)中,work方法依赖于teacher实现;在workB(例子2)中,work转而依赖抽象,这样可以把需要的对象通过参数传入。上述代码通过接口,实现了一定程度的解耦,但仍然是有限的。不仅是使用接口,使用工厂等也能实现一定程度的解耦和依赖倒置。

    在workB中,teacher实例通过set方法传入,从而实现了工厂模式。由于这样的实现仍然是硬编码的,为了实现代码的进一步扩展,把这个依赖关系写在配置文件里,指明workB需要一个teacher对象,专门由一个程序配置是否正确(如所依赖的类文件是否存在)以及加载配置中所依赖的实现,这个检测程序,就称为IOC容器(这里不清楚IOC的小伙伴可以自行谷歌)。

    什么是接口隔离原则?

    其核心思想是:使用多个小的专门的接口,而不要使用一个大的总接口(只需要关心接口,不需要关心实现)。

    接口隔离原则体现在:

    1.接口应该是内聚的,应该避免 “胖” 接口。

    2.不要强迫依赖不用的方法,这是一种接口污染。

    3.表明客户端不应该被强迫实现一些不会使用的接口,应该把胖接口分组,用多个接口代替它,每个接口服务于一个子模块。简单地说,就是使用多个专门的接口比使用单个接口要好很多。

    隔离的手段主要有以下两种:
    1、委托分离,通过增加一个新的类型来委托客户的请求,隔离客户和接口的直接依赖,但是会增加系统的开销(设计模式中,如:代理模式,策略模式中都用到了委托的概念,好奇的小伙伴可以自行谷歌,这里就不贴代码了)。

    2、多重继承分离,通过接口多继承来实现客户的需求,这种方式是较好的。

    胖接口的实例说明

    <?php
    interface Animal{
      public function walk();
      public function speak();
    }
    
    //实现狗的一个接口
    
    class Dog implements Animal{
      public function walk(){
        echo "dogs can walk";
      }
      public function speak(){
        echo "dogs can speak";
      }
    }
    
    //ok,现在我们想创建一个鱼类,它会游泳,怎么办呢?
    //我们必须要修改接口,还会影响到dog类的实现,而fish也需要实现walk和speak方法,如下代码所示:
    
    interface Animal{
      public function walk();
      public function speak();
      public function swim();
    }
    
    //修改后的Gog类
    class Dog implements Animal{
      public function walk(){
        echo "dogs can walk";
      }
      public function speak(){
        echo "dogs can speak";
      }
      public function swim(){
      }
    }
    
    //鱼类
    class Fish implements Animal{
      public function walk(){
      }
      public function speak(){
      }
      public function swim(){
        echo "fish can swim";
      }
    }

    这时Animal接口类就呈现出了”胖“接口的特征了。所谓胖接口其实就是接口中定义了不是所有实现类都需要的方法,就像Animal接口类,有些动物是不会游泳的,有些动物是不会行走的,还有些动物是不会飞的。如果将这些方法都写在一个Animal接口类中,那么后期的扩展和维护简直就是一场灾难。

    那么,怎么解决以上问题呢?

    很简单,接口细化即可,将Animal接口类拆分成三个接口类,然后用多继承分离接口就ok了。

    <?php
    interface animalCanSpeak{
      public function speak();
    }
    
    interface AnimalCanSwim{
      public function swim();
    }
    
    interface animalCanSpeak{
      public function speak();
    }
    
    //定义好这几个接口类之后,dog和fish的实现就容易多了
    
    //狗类
    class Dog implements animalCanSpeak,animalCanWalk{
      public function walk(){
        echo "dogs can walk";
      }
      public function speak(){
        echo "dogs can speak";
      }
    }
    
    //鱼类
    class Fish implements AnimalCanSwim{
      public function swim(){
        echo "fish can swim";
      }
    }

    接口隔离原则(Interface Segregation Principle, ISP)的概念:使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口。

    在使用接口隔离原则时,我们需要注意控制接口的粒度,接口不能太小,如果太小会导致系统中接口泛滥,不利于维护;接口也不能太大,太大的接口将违背接口隔离原则,灵活性较差,使用起来很不方便。一般而言,接口中仅包含为某一类用户定制的方法即可,不应该强迫客户依赖于那些它们不用的方法。

    结语

    实践出真理

    以上就是面向对象的三大特性和五大原则是什么的详细内容,更多请关注php中文网其它相关文章!

    声明:本文原创发布php中文网,转载请注明出处,感谢您的尊重!如有疑问,请联系admin@php.cn处理
    上一篇:PHP 7.4 新语法:数组延展操作符 下一篇:php哪个函数能取得字符串长度
    大前端线上培训班

    相关文章推荐

    • 聊聊php面向对象的编程基础(一)• 聊聊php面向对象的编程基础(二)• php面向对象和面向过程有什么区别

    全部评论我要评论

  • 取消发布评论发送
  • 1/1

    PHP中文网