精华内容
下载资源
问答
  • 面向对象五大原则

    千次阅读 2018-08-05 17:56:46
    一,面向对象五大原则 1.1,理解设计模式与设计原则 软件设计原则:原则为我们提供指南,它告诉我们什么是对的,什么是错的。它不会告诉我们如何解决问题。它仅仅给出一些准则,以便我们可以设计好的软件,...

    一,面向对象五大原则

    1.1,理解设计模式与设计原则

    软件设计原则:原则为我们提供指南,它告诉我们什么是对的,什么是错的。它不会告诉我们如何解决问题。它仅仅给出一些准则,以便我们可以设计好的软件,避免不良的设计。
    软件设计模式:模式是在软件开发过程中总结得出的一些可重用的解决方案,它能解决一些实际的问题。一些常见的模式,比如工厂模式、单例模式等等。
    封装、继承、多态只是类的三大特性,在程序设计时并不是说使用到了这三个特性就是面向对象,真正的面向对象的设计要满足下面五个原则。

    1.2,单一功能原则

    原理:单一职责原则可以看作是低耦合、高内聚在面向对象原则上的引申,将功能定义为引起变化的原因,功能过多可能引起它变化的原因就越多,这将导致功能依赖,相互之间就产生影响,从而极大的损伤其内聚性和耦合度。因此不要为类实现过多的功能,以保证一个类只有一个引起它变化的原因。
    实例:做一个数据库管理系统,根据不同的权限对数据库进行数据删改查的操作,下面是一个很low的设计。

    class DBManager{
    private:
        string userId;
    public:
        DBManager(const string &str):userId(str){}
        void add(){
            if(userId == "chongchong"){
                //执行往数据库里添加数据的操作
            }
        }
    };

    程序分析:

    上面这个是很low的设计,如果验证用户权限的规则或数据库的操作发生改变,那就必须对DBManager类进行修改。权限判断的功能和数据库操作的功能被放在一个类中。我们可以使用Proxy模式,实现权限判断与数据库操作功能的分离。DBManager类实现数据库操作,代理Proxy类里面进行权限判断,下面这个是比较高大上的设计:

    class Protocal{
    public:
        virtual void add();
    };
    
    class DBManager : public Protocal{
    private:
        string userId;
    public:
        DBManager(const string &str):userId(str){}
        void add(){
            //添加一条记录到数据库中
        }
    };
    
    class Proxy : public Protocal{
    private:
        DBManager &manager;
    public:
        Proxy(const DBManager &db):manager(db){}
        void add(){
            //先对用户的权限进行验证,然后再执行往数据库添加数据的操作
            manager.add();
        }
    };
    
    int main() {
        DBManager manager("123456");
        Proxy delegate(manager);
        delegate.add();
        return 0;
    }

    1.3,开放封闭原则

    原理:对扩展是开放的,对修改是封闭的。开放封闭原则主要体现在下面两个方面,一是:对扩展开放意味着,软件有新的需求或变化时,可以对现有的代码进行扩展,以满足新的需求。二是:对修改封闭意味着,类一旦设计完成,就不要对类进行任何的修改。“需求总是变化”、“世界上没有一个软件是不变的”,这些言论是对软件需求最经典的表白。对于软件设计者来说,必须在不需要对原有的系统进行修改的情况下,实现灵活的系统扩展。而如何能做到这一点呢? 要面向接口编程,而不是面向实现编程。实现开放封闭的核心思想就是对抽象编程,而不对具体编程,因为抽象相对稳定。让类依赖于固定的抽象,所以对修改就是封闭的;而通过面向对象的继承和对多态机制,通过接口可以派生出新的类,实现新功能的扩展,所以对于扩展就是开放的,这是实施开放封闭原则的基本思路。
    实例:设计一款射击游戏,在这个游戏中会出现不同的人物角色以及不同的枪支,人物与枪支是最容易扩展的部分,把他们隔离开来,形成统一的接口处理。具体的人物角色与枪支都是依赖于这些接口,此时对接口的修改就是封闭的,而通过继承从抽象类派生出新的类,就是对扩展的开放,下面就是类的设计:

    class Gun{
    public:
        virtual void kill();
    };
    
    class MachineGun : public Gun{
    public:
        void kill(){
            cout<<"MachineGun."<<endl;
        }
    };
    
    class Character{
    public:
        virtual void shoot(){}
    };
    
    class BadGuy : public Character{
    private:
        Gun &weapon;
    public:
        BadGuy(Gun &gun):weapon(gun){}
        void shoot(){
            cout<<"BadGuy use ";
            weapon.kill();
        }
    };
    
    int main() {
        MachineGun machineGun;
        BadGuy badGuy(machineGun);
        badGuy.shoot();
        return 0;
    }

    输出结果:

    BadGuy use MachineGun.
    
    Process returned 0 (0x0)   execution time : 0.006 s
    Press any key to continue.

    程序分析:

    所有的具体类都是依赖于接口,具体类之间没有耦合在一起。例如:所有的Character的派生类,都是与接口Gun耦合在一起,都使用的是接口中提供的方法。

    1.4,替换原则

    原理:子类应当可以替换父类,并能出现在父类能出现的任何位置上,主要就是继承的体现。继承是一项非常优秀的语言机制,它可以提高代码复用性与代码的可扩展性。这个原则的核心思想就是,良好的继承定义了一个规范。
    实例: CS是一款经典的射击游戏,下面是枪类的实现。
    这里写图片描述

    1.5,依赖倒转原则

    原理: 抽象不能依赖于具体,具体应该依赖于抽象。就是要面向接口编程,而不是面向实现编程。
    实例:假设我们现在要做一个电商系统,会遇到这样一个问题:订单入库。假设系统设计初期,用的是SQL Server数据库。通常我们会定义一个SqlServer类,用于数据库的读写。

    class SqlServer{
    public:
        void add(){
            cout<<"往数据库添加一个订单."<<endl;
        }
    };

    然后定义一个Order类,负责订单的逻辑处理。由于订单要入库,需要依赖于数据库的操作。因此在Order类中,需要定义SqlServer类的变量并初始化。

    class Order{
    private:
        SqlServer *p;
    public:
        Order(){
            p = new SqlServer;
        }
        void add(){
            //先进行订单的逻辑处理,再把这个订单放到数据库
            p->add();
        }
    };

    定义上面两个类很容易实现想要的功能,但是可能有一天不想使用SQL Server数据库,要使用Oracle数据库,那么要重新写一个OracleServer类,然后对Order类进行修改。如果底层数据库换成Mysql,又要进行类似的操作,所以设计的系统的扩展性不强。主要的原因有两个,一是:Order直接依赖于一个具体的类,二是:Order依赖的对象的创建与绑定是在它的内部实现的。高层模块Order类不应该依赖于低层模块SqlServer,两者应该依赖于抽象。可以使用IoC(控制反转)来解决上面的问题。IoC有2种常见的实现方式:依赖注入和服务定位。其中,依赖注入是使用最为广泛,下面将深入理解依赖注入(DI)。

    什么是依赖注入(DI)?

    控制反转(IoC)一种重要的方式,就是将依赖对象的创建和绑定转移到被依赖对象类的外部来实现。在上述的实例中,Order类所依赖的对象SqlServer的创建和绑定是在Order类内部进行的。事实证明,这种方法并不可取。既然,不能在Order类内部直接绑定依赖关系,那么如何将SqlServer对象的引用传递给Order类使用呢?依赖注入(DI),它提供一种机制,将需要依赖(低层模块)对象的引用传递给被依赖(高层模块)对象。通过DI,可以在Order类的外部将SqlServer对象的引用传递给Order类对象。

    使用构造函数来实现依赖注入

    构造函数注入,毫无疑问通过构造函数传递依赖。因此,构造函数的参数必然用来接收一个依赖对象。那么参数的类型是什么呢?具体依赖对象的类型?还是一个抽象类型?根据DIP原则,知道高层模块不应该依赖于低层模块,两者应该依赖于抽象。那么构造函数的参数应该是一个抽象类型。首选,需要定义SqlServer的抽象类型DataAccess。

    class DataAccess{
    public:
        virtual void add(){}
    } ;
    
    class SqlServer : public DataAccess{
    public:
        void add(){
            cout<<"往 SQL 数据库添加一个订单."<<endl;
        }
    };
    
    class Oracle : public DataAccess{
    public:
       void add(){
         cout<<"往 Oracle 数据库添加一个订单."<<endl;
       }
    };

    修改Order类:

    class Order{
    private:
        DataAccess &re;
    public:
        Order(DataAccess &re):re(re){}
        void add(){
            //先进行订单的逻辑处理,再把这个订单放到数据库
            re.add();
        }
    };

    下面是main函数:

    int main() {
        SqlServer sql;         //在外部创建依赖对象
        Order order1(sql);     //通过构造函数注入依赖
        order1.add();
    
        Oracle oracle;         //在外部创建依赖对象
        Order order2(oracle);  //通过构造函数注入依赖
        order2.add();
        return 0;
    }

    输出结果:

    往 SQL 数据库添加一个订单.
    往 Oracle 数据库添加一个订单.
    
    Process returned 0 (0x0)   execution time : 0.009 s
    Press any key to continue.

    程序分析:

    显然,不需要修改Order类的代码,就完成了Oracle数据库的移植,这无疑体现了IoC的精妙。

    1.6,接口分离原则

    原理: 使用多个专门的接口,而不使用单一的总接口,即类不应该依赖那些它不需要的接口。

    class Worker{
    public:
        virtual void eat();
        virtual void work();
    };

    现在有两个类实现了这个接口,一是Manager,另一个是ChengXuYuan,下面是这两个类的实现

    class Manager : public Worker{
    public:
        void eat(){
            cout<<"Manager eat."<<endl;
        }
        void work(){
            cout<<"Manager work."<<endl;
        }
    };
    
    class ChengXuYuan : public Worker{
    public:
        void eat(){
            cout<<"ChengXuYuan eat."<<endl;
        }
        void work(){
            cout<<"ChengXuYuan work."<<endl;
        }
    };

    现在引入一个Robot,来实现上面的Worker接口,work行为对机器人来说是可以接受的,但是eat行为对机器人来说,就非常的不合理。如果直接让Robot实现Worker接口,此时的Robot就被迫使用它用不到的接口,当接口Worker发生变化时,它同样也要跟着改变。使用适配器模式解决上面的问题,下面是代码:

    class EatAdapter : public Worker{
        void eat();
    };
    
    class WorkAdapter : public Worker{
        void work();
    };

    使用适配器模式,把一个大的接口,分成几个小的接口。

    展开全文
  • 面向对象 五大原则

    热门讨论 2018-03-09 09:50:58
    五大原则面向对象技术更加规范,让我们深入了解一下! 单一职责原则 就一个类而言,应该仅有一个引起它变化的原因。 是尽量能让类的变化减少,一个类做好自己的本职工作就好了,别操太多的...

    #前言
    没有规矩,不成方圆!面向对象亦是如此!

    五大原则让面向对象技术更加规范,让我们深入了解一下!


    #单一职责原则

    就一个类而言,应该仅有一个引起它变化的原因。

    是尽量能让类的变化减少,一个类做好自己的本职工作就好了,别操太多的心,从而减少职责耦合!防止设计时产生一些不必要的问题!

    类的职责分离是我们在编程的时候,需要去考虑的问题!如果职责太集中的话,很容易牵一发而动全身!


    #开放—封闭原则 >软件实体(类、模块、函数等)应该可以扩展,但是不可以修改。

    其实单看名字的话,会不会有些矛盾呢?既要开发又要封闭的;但是一看解释就一目了然了。开发是对模块功能扩展来说的,而封闭是对模块功能修改来说的,就好比你买了一个自行车,你感觉它的功能不够强大,你可以给它安装各种配件(开放)使其功能更加强大,但是不能把它车轱辘拆下来(封闭)!

    但是我们在开发的时候,不可能保证自己的类不会发生变化,设计的类不可能完全封闭,就像自行车厂商不可能设计出一种自行车能够满足所有人的需求,所以我们要事先对可能会发生变化的种类进行猜测,然后构造抽象(类或是方法)来隔离那些变化!

    我们应该仅仅对那些频繁发生变化的类做出抽象,所以不是抽象的类越多越好,要根据实际情况,在做决定!


    #依赖倒转原则

    1.高层模块不应该依赖底层模块,两个都应该依赖抽象.

    抽象不应该依赖细节。细节应该依赖抽象这句话的意思就是,我们在制造硬件的时候,不要针对某一块主板制造,如果有一天,这个主板公司倒闭了,那么你的硬件设施不就都凉凉了嘛!我们针对主板和硬件的接口制造,那么之后只要有用这种接口的主板,你的硬件都能买的出去!

    2.抽象不应该依赖细节。细节应该依赖抽象

    抽象不应该依赖细节。细节应该依赖抽象这句话的意思就是,面对接口编程,不面对实现编程(不要想太多),就像你有一份工作,不要去想你是去给谁做,你要把它做好然后收到相应的报酬就ok了!

    此原则是面向对象设计的标志,我们在时间编程中会深深的感受到它无处不在!


    #里式替换原则 >子类必须能偶替换掉它们的父类型。

    在一个程序中,用其子类替换掉它的父类,程序不仅可以正常执行,而且没有任何察觉!

    这里我们经常会说的一个例子,那就是企鹅和鸟:
    我的理解:
    |动物|鸟评判标准|是否属于鸟类|
    |-----|-------|
    |企鹅|会飞|不属于|
    |企鹅|体均被羽,恒温,卵生|属于|
    这个表格代表了我的思维,企鹅属不属于鸟类,关键在于鸟类是如何定义的!


    #迪米特法则

    它也叫最少知识原则,如果两个类不必彼此直接通信,那么这两个类就不应当放生直接的相互作用。如果其中一个类需要直接调用另一类的某一个方法的话,可以通过第三者转发这个调用!

    它的前提,也是我们在编程是需要注意的一个点,就是尽量降低类成员的访问权限,包装好自己的private状态!有需要公开的字段,就通过属性的形式进行体现,不仅降低类之间的耦合程度,还让数据更加安全!

    我的理解就是,两个人能不说话就不说话,如果必须说,可以通过和两个人都熟悉的人(第三者)进行间接沟通,从而减少两个人之间的交集(耦合)!

    (没有分享的原则,我会根据具体的设计模式和大家分享!)


    #后语
    原则是思想的体现,也是行为的规范!

    所以我们不仅需要知道,还要做到!

    Let’s go!


    希望本文对您有所帮助! 有什么不足!欢迎指正! 感觉不错可以赞一下哦!
    展开全文
  • 主要介绍了PHP面向对象五大原则之开放-封闭原则(OCP),简单分析了PHP面向对象开放-封闭原则(OCP)的概念、原理、使用方法及相关注意事项,需要的朋友可以参考下
  • 主要介绍了PHP面向对象五大原则之依赖倒置原则(DIP),简单讲述了依赖倒置原则的概念、原理并结合实例形式分析了php依赖倒置原则相关定义与使用方法,需要的朋友可以参考下
  • 主要介绍了PHP面向对象五大原则之单一职责原则(SRP),结合实例形式详细分析了单一职责原则(SRP)的概念、原理、定于与使用方法,需要的朋友可以参考下
  • 主要介绍了PHP面向对象五大原则之里氏替换原则(LSP),较为详细的分析了里氏替换原则(LSP)的概念、原理并结合实例形式分析了php里氏替换原则(LSP)的简单使用方法,需要的朋友可以参考下

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,315
精华内容 526
关键字:

面向对象五大原则