精华内容
下载资源
问答
  • C++编程原则

    2021-08-06 10:14:10
    编程,边调试,边扩充。先编写出最简单的程序框架,成一个可供运行的程序。 不要企图在一开始就解决所有的细节。类是可扩充的,可以一步步地扩充它的功能。直接写好程序,每一步都要调试,调试通过了前面一步在...

    1、先搭框架,逐步扩充,由简到繁,最后完善。

        边编程,边调试,边扩充。先编写出最简单的程序框架,成一个可供运行的程序。

    不要企图在一开始就解决所有的细节。类是可扩充的,可以一步步地扩充它的功能。直接写好程序,每一步都要调试,调试通过了前面一步在做下一步,步步为营。

    2、类的设计和主函数的精简。

        面向对象的程序设计中,最关键的工作是类的设计,所有的数据和数据的操作都是在类中完成。只要把类定义好,编程工作就简单了。

        主函数精简,只是调用对象的成员函数,完成相关操作。大多数情况下,主函数甚至不出现控制类的结构,而在成员函数中使用控制结构。

    展开全文
  • 本文主要向大家介绍了JAVA语言的对面向对象编程的6个基本原则的理解,通过具体的内容向大家展示,希望对大家学习JAVA语言有所帮助。单一职责原则定义:一个类中应该是一组相关性很高的函数,数据的封装。做法:根据...

    本文主要向大家介绍了JAVA语言的对面向对象编程的6个基本原则的理解,通过具体的内容向大家展示,希望对大家学习JAVA语言有所帮助。

    单一职责原则

    定义:一个类中应该是一组相关性很高的函数,数据的封装。

    做法:根据对业务和需求的理解,去划分一个类,一个函数的职责。

    举例:比如要实现一个加载图片的功能,并在加载图片的时候实现对图片的缓存。这时候至少应该需要两个类去完成这个功能。一个是加载和显示图片的类Imageloader。一个是缓存类ImageCache。如果将两个类写在一起,会导致代码的可读性,灵活性,和扩展性变得很差。假设现在需要修改缓存机制,那么我们只需要修改ImageCache类一个地方就可以了。任何需要使用图片缓存的地方都不再需要修改。

    开闭原则

    定义:对于扩展是开发的,对于修改时封闭的。

    做法:出现新的需求时,尽量(注意是尽量)不要修改原有代码。而是对原有代码进行扩展。比如创建一个新的实现类。

    手段:通过抽象来实现开闭原则。抽象出一个抽象类或是一个接口。高层模块(调用者)通过依赖抽象(下面我们会说到的依赖倒置原则)使用“依赖注入”(在java中就是调用set方法)来遵循开闭原则。

    举例:小强卡车造车厂在建厂初期,只能造大卡车。后来随着时代的变迁,小强造车厂需要造出法拉利。于是小强又创办了一个法拉利造车车间。即不影响造卡车,又能造新的法拉利。但是小强的资源有限,没办法同事维护两个车间,于是他将造车的方法抽象出来,这个车间能够按轮胎,喷漆完成一系列造车动作。在造卡车时用大轮胎(set大轮胎)在造法拉利是用小轮胎(set小轮胎)

    小强说干就干,开始通过抽象升级车间了。然而梦想是美好的,现实是残酷的。因为忽略类里氏替换原则和依赖倒置原则。小强的第一次改造以失败而告终。

    里氏替换原则

    定义:所有引用基类的地方必须能透明地使用其子类。通俗的讲,只要父类能出现的地方就一定可以使用其子类。

    做法,运用抽象,抽出一个抽象的类或是接口。

    举例:这里以安装发动机为例。抽象出来的车间都能安装发动机,可是卡车安装大型慢速发动机,而法拉利需要高级的小型发动机。因为技术原因。小强将安装发动机的功能写死只能安装大型发动机。在造卡车时没有问题。可是一换成造法拉利的时候。却误将大型发动机装在了法拉利上。为此,小强的造车厂无法造出法拉利,大亏了一笔。

    依赖倒置原则

    定义:依赖抽象,不要依赖细节。

    做法:面向接口编程。

    举例:小强看出问题的关键所在,于是改造车间,将安装发动机的功能抽象出来,不再写死安装大型发动机。而是将安装发动机的细节分别交由卡车和法拉利自己去实现。而高层模块(调用者在这里指的是造车工人,因为是工人使用车间造车嘛)只要调用抽象车间,根据需求去set汽车就行了。这样,通过依赖抽象,汽车源源不断的生产了出来。

    这时机智的小强发现,卡车不需要打磨,而法拉利需要打磨。但是因为都在一个车间里,导致造卡车的时候,虽然卡车没有去实现打磨的功能,却还是走类个过场,导致类资源的浪费。这时候小强想到类接口隔离原则。

    接口隔原则

    定义:高层模块(调用者)不应该依赖它不需要的接口

    做法:将非常庞大,臃肿的接口拆分成更小,更具体的接口

    举例:通过对造车过程的观察。小强将大的车间改造成了很多小的车间,有安装发动机的车间,安装轮胎的车间,喷漆的车间,打磨的车间。卡车在经过组装喷漆后就完工了。调用者不再需要打磨车间。又完成了任务又节省了资源。小强瞬间觉得自己机智满分。

    迪米特原则原则

    定义与做法:只与自己的直接朋友对话。类不要去访问那些远离自己的朋友。

    举例:小强的车越造越多,之前用户都是在了解了车的细节后让中间商去为他们买车,为此极为的不方便。幸好这时候中间商告诉用户,只要将车的种类和价格告诉他。他就能到小强那找到一款合适的车给用户。用户很方便的就能得到自己想要的汽车。而且也不再需要到小强造车厂处了解车的细节了。减少了往来的奔波劳累。中间商也明确了自己的定位。

    以上就是职坐标整理发布关于JAVA的介绍,先祝大家对它有了一定的了解吧,了解更多内容,请关注职坐标编程语言JAVA频道!

    展开全文
  • 本文作者是一位有着 20 年编程生涯的骨灰级程序员,从 Basic、C 到 Java、JavaScript,从码农到研究员到 CTO,他一直活跃在编程的第一线。本文是他 20 年职业生涯总结下来的编程原则

    本文作者是一位有着 20 年编程生涯的骨灰级程序员,从 Basic、C 到 Java、JavaScript,从码农到研究员到 CTO,他一直活跃在编程的第一线。本文是他 20 年职业生涯总结下来的编程原则。

    我从 1999 年就开始了编程生涯,到今年已经有 20 多年了。我先是从 Basic 开始,很快转到了 Pascal 和 C 语言,然后又学习了面向对象编程语言 Delphi 和 C++。2006 年,我开始使用 Java,2011 年开始使用 JavaScript。我参与过各个行业的软件开发,从机器人、金融科技、医疗到媒体和通信。我还担任过研究员、CTO、TPM(技术产品经理)、老师、系统架构师和技术负责人,但不管怎样,我一直都在编程。在我参与过的项目当中,有些为数百万人提供服务,有些在发布之前就宣告失败。我做过咨询顾问,还创办过自己的公司。我在开源项目、闭源项目和内部开源项目上花了很多时间,从微控制器到移动应用、桌面应用,再到云服务和无服务器架构。

     

    我把过去 20 年积累的一些最为重要的编程原则总结如下:

    1、不要纠结于开发工具——不管是库、编程语言还是平台。尽可能使用原生的构件。不要歪曲技术,也不要歪曲了问题本身。为要解决的问题选择合适的工具,否则你要为你所选择的工具重新安排你的工作。

    2、你写的代码不是给机器看的,而是给你的同事和未来的你看的(除非你写的是一次性代码或汇编代码)。写代码的时候要考虑一下初级开发者,他们会把你的代码作为参考。

    3、优秀的软件是协作开发的结果。高效沟通,进行开放式的协作。信任他人,并让他人也信任你。尊重他人胜过尊重代码。以身作则,把你的追随者变成领导者。

    4、分而治之。为分离的关注点开发单独的低耦合模块。进行单独的模块测试和集成测试。尽可能按照实际情况测试,同时也要测试到各种边界情况。

    5、不要把自己与代码捆绑在一起,要想办法让其他人也能修改你的代码或者添加新的功能,这样你才能更容易脱身去参与其他项目,或者去其他公司。不要捆绑自己,否则你很难成长。

    6、安全性是分层的,每一层需要进行单独的评估,同时又与整体相关。风险是一个业务决策,与脆弱性和概率有直接的关系。每一个产品或组织都有不同的风险偏好(为了获得更大的收益,他们愿意承担风险)。通常这三个关注点之间存在相互冲突:用户体验、安全性和性能。

     

    7、要意识到每一行代码都有其生命周期,它们最终都会死掉。有时候,一些代码会在发布之前就死掉,所以要学会放手。代码可以分为三种:一种是核心代码,就像汽车的引擎,没有了它,产品就毫无意义;一种是必要的代码,就像是汽车的备胎,平时用得少,但一旦需要,它决定了系统的成败;一种是增值的代码,就像汽车的杯托,如果有那是再好不过,但如果没有也不会影响产品。

    8、不要把你的个人标识融入到代码里,人和代码应该是分离的。不要把其他人对代码的评价与你自身联系到一起,在评价他人的代码时也要十分谨慎。

    9、技术债务就像快餐一样,偶尔欠下一点技术债务是可接受的,但如果你习惯了这样,它会毁掉你的产品(而且是以让你措手不及的方式)。

    10、在寻找解决方案时,请按照这样的优先级进行决策:安全性>可用性(可访问性和用户体验)>可维护性>简单性(开发者体验)>简洁性(代码量)>性能。但不能盲目照搬,而是要根据产品的特点进行取舍。你积累的经验越多,就越是能够在这些因素之间做出权衡。例如,在设计游戏引擎时,性能享有最高的优先级,但在开发银行应用程序时,安全性则最为重要。

    11、拷贝粘贴是滋生 bug 的温床。对你所拷贝或导入的东西加以审查,bug 一般会藏身在复杂性中。依赖项复杂没有关系,但不能让它存在于代码中。

    12、不要只顾着写正常的代码,处理异常的代码也要好好写。让人们明白为什么会发生异常、如何检测到的以及怎样解决。对所有的系统输入(包括用户输入)进行验证:尽早失败,并尽可能从错误中恢复。我们要假设用户手里握着一把枪:你努力让用户输入一些其他的东西,而不是让他们的子弹射在你的脑门上。

    13、不要使用依赖项,除非使用它们的成本比你自己写代码的成本低很多。因为使用依赖项要导入、维护、处理 bug,在必要的时候还要进行重构,这些都是成本。

     

    14、远离“炒作驱动开发”,但你要去了解它们,做一些尝试。

    15、走出舒适区,每天都要学习。把学到的东西分享出来。如果你以大师自居,就不是在学习。接触更多的编程语言、技术、文化,保持一颗好奇心。

    16、好代码不需要注释,而优秀的代码提供了良好的注释,可以让任何一个原先没有参与代码演进、试错和需求过程的人更容易阅读、修改它。

    17、尽量避免使用重载、继承和隐式的智能特性。使用纯函数,它们更容易测试和诊断,否则的话就使用类。实现不同功能的函数要使用不同的名字。

    18、在彻底了解问题之前不要急着写代码。花在倾听和了解问题上的时间通常比花在写代码上的时间要多。在写代码之前要先了解问题域。问题就像迷宫一样,你要循序渐进,反复进行“编码 - 测试 - 改进”,直到把问题解决为止。

    19、不要尝试去解决不存在的问题。不要进行投机性编程。只有在确定代码确实需要具备扩展性之后才让代码具备可扩展性。通常情况下,当代码被扩展之后,你会发现问题会变得与原先认为的不一样了。

    20、大家一起开发软件会更加有趣。建立可持续发展的社区。倾听,激发灵感,学习,分享。

    我并不是软件开发方面的权威,但这些都是我一路走来总结出来的原则。我相信,20 年后,这些原则会发生变化,会变得更加成熟。

     

    如果你想更好的提升你的编程能力,学好C语言C++编程!弯道超车,快人一步!
    C语言C++学习企鹅圈子】,分享(源码、项目实战视频、项目笔记,基础入门教程)
    欢迎转行和学习编程的伙伴,利用更多的资料学习成长比自己琢磨更快哦!

    编程学习书籍:

     

    编程学习视频:

    展开全文
  • Java面向对象编程原则

    2021-10-09 15:00:25
    面向对象编程原则概述 1.1 软件的可维护性和可复用性 可维护性较低的软件设计,通常由于如下四个原因造成: 过于僵硬(Rigidity) :灵活性不够 过于脆弱(Fragility) :健壮性(鲁棒性) 不够复用率低(Immobility) :不能...

    面向对象编程原则概述

    1.1 软件的可维护性和可复用性

    可维护性较低的软件设计,通常由于如下四个原因造成:

    • 过于僵硬(Rigidity) :灵活性不够
    • 过于脆弱(Fragility) :健壮性(鲁棒性)
    • 不够复用率低(Immobility) :不能重用
    • 黏度过高(Viscosity) :高耦合,关联性太高

    一个好的系统设计应该具备如下三个性质:

    • 可扩展性(Extensibility)
    • 灵活性(Flexibility)
    • 可插入性(Pluggability)

    程序设计原则:高内聚,低耦合

    面向对象编程的七大原则

    注意:这七个原则并非是孤立的。这7个原则是一个整体。

    单一职责原则

    定义:

    单一职责原则(Single Responsibility Principle, SRP)定义如下:在软件系统中,一个类只负责一个功能领域中的相应职责。

    分析:

    ​ 一个类(或者大到模块,小到方法)承担的职责越多,它被复用的可能性越小。而且如果一个类承担的职责过多,就相当于将这些职责耦合在一起,当其中一个职责变化时,可能会影响其他职责的运作。
    ​ 类的职责主要包括两个方面:数据职责和行为职责,数据职责通过其属性来体现,而行为职责通过其方法来体现。
    ​ 单一职责原则是实现高内聚√低柄甘h要设计人员发现类的不同职贡并将其分离,而发存在,它是最简单但又最难运用的原则,需要设计人员发现类的不同职责并将其分离,而发现类的多重职责需要设计人员具有较强的分析设计能力和相关重构经验。

    示例:

    ​ 编写一个程序:实现两个数+,-,*,/运算,并输出结果

    程序1:

    public class Cal {
    	public static void main(String[] args) {
    		Scanner scan = new Scanner(System.in);
       		System.out.print("输入num1 : ");
    		double num1 = scan.nextDouble();
            System.out.print("输入运算符:");
            String oper = scan.next();
    		System.out.print("输入Num2: ");
            double num2 = scan.nextDouble();
            
    		if(oper.equals("+")){         
    			System.out.println(num1+num2);
            }else if(oper.equals("-")){
    			System.out.println(num1-num2);
            }else if(oper.equals("*")){
    			System.out.println(num1*num2);
            }else if(oper.equals("/")){
                System.out.println(num1/num2);
            }
        }
    }
    
    
    

    程序分析:

    ​ 1、这个方法的职责有:获取数据(从控制台),判断运算符,执行计算,输出结果。
    ​ 2、这个万法巴d田加法运算。这时,上面的程序就需要去修改整个程序。这就会有发一个计算器,需要使用加法运算。这时,上面的程序完全无法使用。(复用率低下)
    ​ 3、如果要修改某个计算或者要增加某个计算,那么就需要去修改整个程序。这就会有程序修改的风险。

    程序优化:

    ​ 1、先考虑程序的复用率,使用单一职责原则来进行优化。
    ​ 2、获取数据和输出结果与程序开发的平台有关。(可以将这两个功能放到一个类中3、具体的计算,可以每一个计算都进行封装。
    ​ 4、判断运算符(程序逻辑),可以进行封装.优化后的类图:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dnE1n0E0-1633762813697)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20201202222928641.png)]

    优化后的程序:

    //负责加法运算
    public class Add {
        public double cal( double num1, double num2){
       		 return num1+num2;
    	}
    }
    
    //负责减法运算
    public class Sub {
        public double cal( double num1, double num2){
       		 return num1-num2;
    	}
    }
    
    //负责判断运算符,并调用具体的运算类
    public class Cal{
        public double oper( String oper , double num1, double num2){
            double rst = 0;
            if(oper.equals("+")){
                Add add = new Add();
                rst = add.cal(num1,num2) ;
            }else if(oper.equals("-")){
                sub sub = new Sub();
                rst = sub.cal( num1, num2);
            }
            return rst;
        }
    }
    
    //负责数据的收集与显示
    public class MainTest{
        public static void main(String[] args) i
            //收集数据
            Scanner scan = new Scanner(System.in) ;
            System.out.print("输入num1: ");
            double num1 = scan.nextDouble();
            System.out.print("输入运算符:");
            String oper = scan.next();
            system.out.print("输入Num2: ");
            double num2 = scan.nextDouble();
            //调用cal类得到计算结果
            cal cal = new Cal();
            double rst = cal.oper(oper,num1,num2);
            //输出结果
        	System.out.println(rst)
    	}
    }
    

    优化后的程序分析:

    1、整体的计算功能可移植(可复用)。将具体的业务逻辑和显示平台进行分离。业务逻辑和具体的展示无关。于是这个逻辑就可以使用到任何的展示平台中。
    2、具体的计算进行了分离。每一个计算都可复用。
    3、分类了具体的计算,那么如果要对其中某一个计算进行修改,就不会影响到其他的计算对象。(提高了程序的可维护性)

    使用单一职责的好处

    1. 类的复杂性降低,实现什么职责都有清晰明确的定义2.可读性提高,复杂性降低,那当然可读性提高了
    2. 可维护性提高,那当然了,可读性提高,那当然更容易维护了
    3. 变更引起的风险降低,变更是必不可少的,如果接口的单一职责做得好,一个接口修改
    4. 只对相应的实现类有影响,对其他的接口无影响,这对系统的扩展性、维护性都有非常大帮助
    5. 注意:
      单一职责的应用应该要有一个合理的限度,并不是越单一越好。如果类的功能越单一,那么就会产生越多的类,类的数量越多就会造成整个系统的结构越复杂。所以我们在设计系统的时候需要找到一个合理的平衡点。

    开放封闭原则

    开闭原则定义

    一个软件实体应当对扩展开放,对修改关闭。也就是说在设计一个模块的时候,应当使这个模块可以在不被修改的前提下被扩展,即实现在不修改源代码的情况下改变这个模块的行为
    1、对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。
    2、对修改封闭,意味着类一旦设计完成,就可以独立完成其工作,而不要对其进行任何的修改。

    如何实现开放封闭原则

    实现开闭原则的核心思想就是对抽象编程,而不对具体编程,因为抽象相对稳定。让类依赖于固定的抽象,所以修改就是封闭的;而通过面向对象的继承和多态机制,又可以实现对抽象类的继承,通过覆写其方法来改变固有行为,实现新的拓展方法,所以就是开放的。

    软件设计要容易维护又不容易出问题的最好方法就是多扩展、少修改

    使用开闭原则优化计算机项目

    //负责判断运算符,并调用具体的运算类
    public class Cal{
        
        public double oper( String oper , double num1, double num2){
            double rst = 0;
            if(oper.equals("+")){
                Add add = new Add();
                rst = add.cal(num1,num2) ;
            }else if(oper.equals("-")){
                sub sub = new Sub();
                rst = sub.cal( num1, num2);
            }
            return rst;
        }
    }
    

    分析:

    1、在上面的代码中,计算的种类可能增加,可能减少。如果要增加一个乘法类,那么就必须要修改oper方法。而且这中修改在目前的程序结构下是无法避免的。

    2、现在要变更的最有可能的地方是:每一个计算类(增加,减少,修改)

    3、对计算方法进行抽象,然后在oper类中使用多态来封闭这种变化

    优化后的程序:

    //定义一个计算接口
    public interface Cal {
        public double cal(double num1,double num2);
    }
    
    //加法实现类
    public class Add implements Cal {
        @Override
        public double cal(double num1, double num2) {
            return num1+num2;
        }
    }
    
    //减法实现类
    public class Sub implements Cal {
        @Override
        public double cal(double num1, double num2) {
            return num1-num2;
        }
    }
    
    //工厂类
    public class Factory {
        public static Cal createInstance(String oper){
            Cal cal = null;
            switch (oper){
                case "+":
                    cal = new Add();
                    break;
                case "-":
                    cal = new Add();
                    break;
            }
            return cal;
        }
    }
    
    public class MainTest {
        public static void main(String[] args) {
            //收集数据
            Scanner input = new Scanner(System.in);
            System.out.println("输入num1");
            double num1 = input.nextDouble();
            System.out.println("输入运算符");
            String oper = input.next();
            System.out.println("输入num2");
            double num2 = input.nextDouble();
    
            //调用工厂类的方法获取具体的计算实例
            Cal cal = Factory.createInstance(oper);
            double result = cal.cal(num1, num2);
            //输出结果
            System.out.println(result);
        }
    }
    

    程序分析:
    1、上面的程序中,如果要增加一个乘法计算。需要增加一乘法类,然后修改Factory类。
    2、在factory类中使用了多态特性。客户端只需要向Factory类发起请求,Factory类会向客户端返回一个计算对象。(客户端不用去管这个计算对象是怎么来的>
    3、在这个程序中,Factory中的判断修改没有封闭掉。但是可以使用反射和配置文件的方式将这种变化放到配置文件中。(将变化放到配置文件中,好处在于:配置文件是可以随时修改的,而程序修改后需要重新编译和发布)

    oper.properties配置文件:

    +=com.zsn.test.Add
    -=com.zsn.test.sub
    
    //工厂类
    public class Factory {
        public static Cal createInstance(String oper) {
            Cal cal = null;
            try {
                //加载配置文件
                Properties properties = new Properties();
                properties.load(Factory.class.getClassLoader().getResourceAsStream("oper.properties"));
                //获取需要执行类的全类名
                String clz = properties.getProperty(oper);
                System.out.println(clz);
                //动态加载
                Class clazz = Class.forName(clz);
                //创建实例
                 cal = (Cal)clazz.newInstance();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return cal;
        }
    }
    

    4、上面这种程序结构称为:简单工厂模式

    依赖倒置原则

    依赖倒置原则的定义

    依赖倒置原则的核心思想是:依赖于抽象。具体而言就是高层模块不依赖于底层模块,二者都同依赖于抽象;抽象不依赖于具体,具体依赖于抽象。

    依赖倒置原则分析

    依赖一定会存在于类与类、模块与模块之间。当两个模块之间存在紧密的耦合关系是,最好的方法就是分离接口和实现:在依赖之间定义一个抽象的接口使得高层模块调用接口,而底层模块实现接口的定义,以此来有效控制耦合关系,达到依赖于抽象的设计目标。
    抽象的稳定性决定了系统的稳定性,因为抽象是不变的,依赖于抽象是面向对象精髓,也是依赖倒置原则的核心。
    依赖于抽象是一个通用的原则,

    依赖于抽象是一个通用的原则,而某些时候依赖于细节则是在所难免的,必须权衡在抽象和具体之间的取舍,方法不是一层不变的。依赖于抽象,就是对接口编程,不要对实现编程。

    开发封闭原则是面向对象程序设计的原则,而依赖倒置原则类似于实现开放封闭原则的方法。

    依赖倒置原则示例

    示例一、一个拿c照的司机能够开奔驰小车

    //定义奔驰小车类
    public class Benz {
    	public void run(){
    		System.out.println("大奔来了,前面的让一让……");
    	}
    }
    
    //定义一个司机类(能开奔驰小车)
    public class Driverc {
    	public void drive(Benz benz){
    		System.out.println("老司机开车了,上车的赶快……");
            benz.run();
    	}
    }
    
    public class Client {
    	public static void main( String[] args){
    		Benz benz = new Benz( );
    		Driverc driver = new DriverC();
            //让司机去开奔驰车
    		driver.drive(benz) ;
    	}
    }
    
    

    程序分析:
    上面的程序实现了拿C照的司机能够开奔驰小车。那么拿C照的司机就只能开奔驰小车吗?
    拿C照的司机还能够开BMW,Auto…
    但是在上面的程序中,司机类已经依赖了Benz类**(依赖与具体的Benz类,违反了依赖倒置原则)。那么如果要开BMW,就需要修改上面的程序。(违反了开放封闭原则)**

    程序优化:可以将车抽象出来,让司机依赖于一个抽象的车

    //定义小车接口
    public interface Car {
    	public void run();
    }
    
    //定义奔驰小车类(实现car接口)
    public class Benz implements Car{
    	@override
    	public void run(){
    		System.out.print1n("大奔来了,前面的让一让……");
    	}
    }
    
    
    //BMW类,实现car接口
    public class BMw implements Car i
    	@override
    	public void run() {
    		System.out.println("没见吗?别摸我……");
    	}
    }
    
    //定义一个司机类(能开小车)
    public class Driverc {
    	public void drive(car car){
    		System.out.println("老司机开车了,上车的赶快……");
            car.run();
    }
    
    public class client {
    	public static void main(String[] args) i
    		Car car1 = new Benz();
    		Car car2 = new BMIW();
    		Driverc driver = new DriverC();
       		 //让司机去开奔驰车
    		driver.drive(car1);
        	//让司机去开BMW
        	driver.drive(car2);
    	}
    }
    

    程序分析:
    利用依赖倒置原则将程序进行优化后,能到达到拿c照的司机可以开任何的小车。程序的可扩展性就大大提高了。并且封闭了换车的变更。(利用了依赖倒置原则之后,也是实现了开放封闭原则)

    里氏替换原则

    定义

    里氏替换原则的核心思想是:子类必须能够替换其基类。这一思想体现为对继承机制的约束规范,只有子类能够替换基类时,才能保证系统在运行期内识别子类,这是保证继承复用的基础。在父类和子类的具体行为中,必须严格把握继承层次中的关系和特征,将基类替换为子类,程序的行为不会发生任何变化。同时,这一约束反过来则是不成立的,子类可以替换基类,但是基类不一定能替换子类(因为子类继承父类之后,可能会扩充一些自己所特有的属性和方法——这是父类将无法完全替换子类)。

    基于该原则可知:在继承时,子类尽量不要重写父类的方法。

    经典问题

    1、鸵鸟飞鸟:

    比如“鸟”是基类,这个基类有一个“飞翔”的行为。当“鸵鸟”继承了“鸟”,这就会引起麻烦,覆写基类“飞翔”的行为吧,这样就不再符合里氏替换原则。“鸵鸟”是不能替换它的基类了。

    2、正方形不是长方形

    “长方形”是基类,“正方形”是一种特殊的长方形,理所应当“正方形”是“长方形”的子类。“长方形”有单独改变长或宽的行为,对于“正方形”来说,就得改写这两个行为以保证长等于宽。这样就违背了里氏替换原则。当长方形调整了长,又调整宽,在算面积的时候。正方形这个子类就会出错。

    分析

    Liskov替换原则,主要着眼于多态建立在继承的基础上,因此只有遵循了Liskov替换原则,才能保证继承复用是可靠的。
    实现的方法是面向接口编程:将公共部分抽象为基类接口或抽象类,通过继承父类,在子类中通过覆写父类的方法实现新的方式支持同样的职责。

    Liskov替换原则是关于继承机制的设计原则,违反了Liskov替换原则就必然导致违反开放封闭原则。
    Liskov替换原则能够保证系统具有良好的拓展性,同时实现基于多态的抽象机制,能够减少代码冗余,避免运行期的类型判别。

    代码:

    package com.zsn.test;
    
    public class A {
        public void fun(int a, int b) {
            System.out.println(a + "+" + b + "=" + (a + b));
        }
    }
    
    class B extends A {
        @Override
        public void fun(int a, int b) {
            System.out.println(a + "-" + b + "=" + (a - b));
        }
    }
    
    class demo {
        public static void main(String[] args) {
            System.out.println("父类的运行结果");
            A a = new A();
            a.fun(1, 2);
            //父类存在的地方,可以用子类替代
            //子类B替代父类A
            System.out.println("子类替代父类后的运行结果");
            B b = new B();
            b.fun(1, 2);
        }
    }
    
    

    接口隔离原则

    定义

    接口隔离原则的核心思想是:使用多个小的专门的接口,而不要使用一个大的总接口。

    分析

    ​ 接口隔离原则体现在:接口应该是内聚的,应该避免“胖”接口。一个类对另外一个类的依赖应该建立在最小的接口上,不要强迫依赖不用的方法,这是一种接口污染
    ​ 接口隔离强调接口的单一性。而胖接口存在明显的弊端,会导致实现的类型必须完全实现接口的所有方法等(从语法上来说:一个类实现一个接口那么就必须要实现接口中的所有方法);而某些时候,实现类型并非需要所有的接口定义,在设计上这是“浪费”,而且在实施上这会带来潜在的问题,对胖接口的修改将导致一连串的客户端程序需要修改,有时候这是一种灾难。在这种情况下,将胖接口分解为多个特点的定制化方法,使得客户端仅仅依赖于它们的实际调用的方法,从而解除了客户端不会依赖于它们不用的方法(将胖接口进行拆分)。

    分离的手段主要有以下两种:

    1、委托分离,通过增加一个新的类型来委托客户的请求,隔离客户和接口的直接依赖,但是会增加系统的开销。

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

    代码:

    /**
    * 定义接口1,里面定义了N个方法
    */
    public interface Interface1{
        void fun1();
    	void fun2();
        void fun3();
        void fun4();
        void fun5();
    }
    

    现在有一个实现类 需要fun1,fun2两个方法,那么当这个实现类实现interface1的时候却实现了5个方法

    /**
    * Class1类中具有多个无用的方法。
    * 虽然在这里可以不用去写太多的代码,但是在调用的时候会引起混乱
    */
    public class Class1 implements Interface1{
    	@Override
    	public void fun1() {
    		system.out.println("这个方法是有用的.....");
    	}
    	@Override
    	public void fun2() {
    		System.out.println("这个方法是有用的.....");
    	}
    	@Override
    	public void fun3() {
            
        }
        @Override
    	public void fun4() {
            
        }
        @Override
    	public void fun5() {
            
        }
    }
    

    利用接口隔离原则优化代码:

    /**
    * 首先将胖接口根据具体的需求隔离成多个小的接口
    */
    public interface Interface1{
        void fun1();
    	void fun2();
    }
    
    public interface Interface2{
        void fun3();
    	void fun4();
    }
    
    public interface Interface3{  
    	void fun5();
    }
    
    //如果一个类需要实现方法fun1,fun2,fun5。再定义一个接口,继承接口1和接口3
    //这时,接口4利用接口的多继承特性,就具有了三个方法的定义
    public interface Interface4 extends Interface1, Interface3{  
    
    }
    
    //实现类实现接口4
    public class Class1 implements Interface4{
    	@Override
    	public void fun1() {
    		
    	}
    	@Override
    	public void fun2() {
    		
    	}
        @Override
    	public void fun5() {
            
        }
    }
    

    接口隔离注意事项

    接口尽量小,但是要有限度。对接口进行细化可以提高程序设计灵活性是不争的事实,但是如果过小,则会造成接口数量过多,使设计复杂化。所以一定要适度。
    为依赖接口的类定制服务,只暴露给调用的类它需要的方法,它不需要的方法则隐藏起来。只有专注地为一个模块提供定制服务,才能建立最小的依赖关系。提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情

    合成复用原则

    合成复用原则定义:

    合成复用原则又被称为组合/聚合复用原则,其定义如下:

    尽量使用对象组合,而不是继承来达到复用的目的。也就是,不要为了继承而继承。

    复用的示例:

    在此,以数据访问层获取数据库连接,同时操作用户数据为例。
    创建一个数据库连接类,内部提供一个获取数据库连接的方法

    public class DBConnection {
    	public String getConnection() {
    	return "MySQL数据库连接";
    	}
    }
    

    创建一个用户数据访问类,基础数据库连接类的同时,提供一个新增用户的方法。

    public class UserDao extends DBConnection {
    	public void addUser(){
    	String connection = super.getConnection();//省略后续添加客户的操作
    	}
    }
    

    测试:

    public class Test {
    	public static void main(String[] args){
    		UserDao userDao = new UserDao();
         	userDao.addUser();
    	}
    }
    

    输出:

    使用MySQL数据库连接添加用户

    新的需求
    假设现在新旧系统合并,需要将旧有系统的Oracle 数据库中的数据接入到新系统中。
    但是,目前DBConnection 中只会返回MySQL数据库的连接。
    其实也简单,我们在DBConnection中再新增一个方法用于获取 Oracle 数据库的连接。功能实现上其实是没什么问题的,但是这样会违反开闭原则。
    那如何利用合成复用原则对示例代码进行重构?

    这里数据库连接类是一个抽象类,内部包含一个获取数据库连接的抽象方法,具体获取哪种数据库的连接则交于具体的子类去实现。

    public abstract class DBConnection {
    	public abstract String getConnection();
    }
    

    创建具体的数据库连接子类

    public class MySQLConnection extends DBConnection {
    	@Override
    	public String getConnection() {
    		return "MySQL数据库连接";
    }
    
    public class OracleConnection extends DBConnection {
    	@Override
    	public String getConnection() {
    		return "Oracle数据库连接";
    }
    

    通过组合的方法将数据库连接注入到用户数据访问层当中。

    public class UserDao {
    	private DBConnection connection;
        
    	public void setConnection(DBConnection connection) {
    		this.connection = connection;
    	}
    	public void addUser() {
    	//省略后续利用connection添加客户的操作
    	}
    }
    
    

    测试:

    public class Test {
    public static void main(String[] args) {
    	UserDao userDao = new UserDao();
    	userDao.setConnection(new MySQLConnection());
        userDao.addUser();
        
    	userDao.setConnection(new OracleConnection());
        userDao.addUser();
    	}
    }
    

    输出:

    使用MySQL数据库连接添加用户

    使用Oracle数据库连接添加用户

    小结:

    • 使用继承的方式来进行代码复用,缺点在于继承来的方法相当于是自身的方法,那么这个方法如果在需求发生变更的时候,就无法改变(必须要修改源代码来达到目的)。这破坏开放封闭原则。
    • 而使用合成(组合)复用,方法是其他类中的方法,在本类中只是去调用其他的类。所以可以使用依赖倒置原则,可以达到在需求发生变更的时候,扩展现有代码达到目的。

    合成复用原则分析

    1、合成复用原则就是指在一个新的对象里通过关联关系(包括组合关系和聚合关系)来使用一些已有的对象,使之成为新对象的一部分;新对象通过委派调用已有对象的方法达到复用其已有功能的目的。简言之:要尽量使用组合/聚合关系,少用继承。
    2 、在面向对象设计中,可以通过两种基本方法在不同的环境中复用已有的设计和实现,即通过组合/聚合关系或通过继承。

    - 继承复用:实现简单,易于扩展。破坏系统的封装性,从基类继承而来的实现是静态的,不可能在运行时发生改变,没有足够的灵活性;只能在有限的环境中使用。(“白箱”复用)
    
    • 组合/聚合复用:耦合度相对较低,选择性地调用成员对象的操作;可以在运行时动态进行。(“黑箱”复用)

    3、组合/聚合可以使系统更加灵活,类与类之间的耦合度降低,一个类的变化对其他类造成的影响相对较少,因此一般首选使用组合/聚 合来实现复用;

    其次才考虑继承,在使用继承时,需要严格遵循里氏代换原则,有效使用继承会有助于对问题的理解,降低复杂度,而滥用继承反而会增加系统构建和维护的难度以及系统的复杂度,因此需要慎重使用继承复用。

    迪米特法则

    迪米特法则定义

    迪米特法则(Law of Demeter, LoD)又称为最少知识原则(Least Knowledge Principle,LKP),

    它有多种定义方法,其中几种典型定义如下:

    (1)不要和“陌生人”说话。
    (2)只与你的直接朋友通信。
    (3)每一个软件单位对其他的单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位。

    迪米特法则示例:

    示例场景:老板让超市员工查看商品的总数量

    商品类

    public class Goods{
        
    }
    

    员工类

    public class Staff {
    	public void checkNumber(List<Goods> goods) {
    		System.out.println("目前超市内商品总数为: "+goods.size());
    	}
    }
    

    老板类

    public class Boss {
    	public void requireCheckNumber(Staff staff) {
    		List<Goods> goodsList = new ArrayList<Goods>();
            for (int i = 0; i<50; i++) {
    			goodsList.add(new Goods());
    		}
    		staff.checkNumber(goodsList);	
    	}
    }
    

    测试类

    public class Test{
        public static void main(String[] args){
            Boss boss = new Boss();
            Staff staff = new Staff();
    		boss.requireCheckNumber(staff);
    	}
    }
    

    问题分析:
    老板指挥员工做事情(清点商品)。但是,在老板内中居然出现了商品类Goods。现在如下修改。

    老板类

    public class Boss {
    	public void requireCheckNumber(Staff staff) {
    		staff.checkNumber();
    	}
    }
    

    员工类

    public class Staff {
    	public void checkNumber() {
    		List<Goods> goodsList = new ArrayList<Goods>();
            for ( int i = 0; i < 50; i++) {
    			goodsList.add(new Goods());
    		}
    		System.out.println("目前超市内商品总数为: "+goodsList.size());
    	}
    }
    

    经过如上的改造,老板类不再与商品类发生直接关系。

    迪米特法则分析

    ​ 1、迪米特法则就是指一个软件实体应当尽可能少的与其他实体发生相互作用(与其他实体发生关系越多,那么耦合度就越高)。这样,当一个模块修改时,就会尽量少的影响其他的模块,扩展会相对容易,这是对软件实体之间通信的限制,它要求限制软件实体之间通信的宽度和深度。
    ​ 2、在迪米特法则中,对于一个对象,其朋友包括以下几类:
    ​ (1)当前对象本身(this);

    ​ (2) 以参数形式传入到当前对象方法中的对象;

    ​ (3)当前对象的成员对象;

    ​ (4) 如果当前对象的成员对象是一个集合,那么集合中的元素也都是朋友;

    ​ (5)当前对象所创建的对象。任何一个对象,

    如果满足上面的条件之一,就是当前对象的“朋友”,否则就是“陌生

    ​ 3、迪米特法则可分为狭义法则和广义法则。在狭义的迪米特法则中,如果两个类之间不必彼此直接通信,那么这两个类就不应当发生直接的相互作用,如果其中的一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。
    ​ 4、狭义的迪米特法则:可以降低类之间的耦合,但是会在系统中增加大量的小方法并散落在系统的各个角落,它可以使一个系统的局部设计简化,因为每一个局部都不会和远距离的对象有直接的关联,但是也会造成系统的不同模块之间的通信效率降低,使得系统的不同模块之间不容易协调。
    ​ 5、广义的迪米特法则:指对对象之间的信息流量、流向以及信息的影响的控制,主要是对信息隐藏的控制。信息的隐藏可以使各个子系统之间脱耦,从而允许它们独立地优化、使用和修改,同时可以促进软件的复用,由于每一个模块都不依赖于其他模块而存在,因此每一个模块都可以独立地在其他的地方使用。一个系统的规模越大,信息的隐藏就越重要,而信息隐藏的重要性也就越明显。

    如何使用迪米特法则

    迪米特法则的主要用途在于控制信息的过载:
    1、在类的划分上,应当尽量创建松耦合的类,类之间的耦合度越低,就越有利于复用,一个处在松耦合中的类一旦被修改,不会对关联的类造成太大波及;

    2、在类的结构设计上,每一个类都应当尽量降低其成员变量和成员函数的访问权限;

    3、在类的设计上,只要有可能,一个类型应当设计成不变类;

    4、在对其他类的引用上,一个对象对其他对象的引用应当降到最低。

    设计原则总结

    单一职责原则:要求在软件系统中,一个类只负责一个功能领域中的相应职责。
    开闭原则要求:一个软件实体应当对扩展开放,对修改关闭,即在不修改源代码的基础上扩展一个系统的行为。
    里氏代换原则:可以通俗表述为在软件中如果能够使用基类对象,那么一定能够使用其子类对象。
    依赖倒转原则:要求抽象不应该依赖于细节,细节应该依赖于抽象;要针对接口编程,不要针对实现编程。
    接口隔离原则:要求客户端不应该依赖那些它不需要的接口,即将一些大的接口细化成一些小的接口供客户端使用。
    合成复用原则:要求复用时尽量使用对象组合,而不使用继承。
    方法的话,可以通过第三者转发这个调用。
    ​ 4、狭义的迪米特法则:可以降低类之间的耦合,但是会在系统中增加大量的小方法并散落在系统的各个角落,它可以使一个系统的局部设计简化,因为每一个局部都不会和远距离的对象有直接的关联,但是也会造成系统的不同模块之间的通信效率降低,使得系统的不同模块之间不容易协调。
    ​ 5、广义的迪米特法则:指对对象之间的信息流量、流向以及信息的影响的控制,主要是对信息隐藏的控制。信息的隐藏可以使各个子系统之间脱耦,从而允许它们独立地优化、使用和修改,同时可以促进软件的复用,由于每一个模块都不依赖于其他模块而存在,因此每一个模块都可以独立地在其他的地方使用。一个系统的规模越大,信息的隐藏就越重要,而信息隐藏的重要性也就越明显。

    如何使用迪米特法则

    迪米特法则的主要用途在于控制信息的过载:
    1、在类的划分上,应当尽量创建松耦合的类,类之间的耦合度越低,就越有利于复用,一个处在松耦合中的类一旦被修改,不会对关联的类造成太大波及;

    2、在类的结构设计上,每一个类都应当尽量降低其成员变量和成员函数的访问权限;

    3、在类的设计上,只要有可能,一个类型应当设计成不变类;

    4、在对其他类的引用上,一个对象对其他对象的引用应当降到最低。

    设计原则总结

    单一职责原则:要求在软件系统中,一个类只负责一个功能领域中的相应职责。
    开闭原则要求:一个软件实体应当对扩展开放,对修改关闭,即在不修改源代码的基础上扩展一个系统的行为。
    里氏代换原则:可以通俗表述为在软件中如果能够使用基类对象,那么一定能够使用其子类对象。
    依赖倒转原则:要求抽象不应该依赖于细节,细节应该依赖于抽象;要针对接口编程,不要针对实现编程。
    接口隔离原则:要求客户端不应该依赖那些它不需要的接口,即将一些大的接口细化成一些小的接口供客户端使用。
    合成复用原则:要求复用时尽量使用对象组合,而不使用继承。
    迪米特法则:要求一个软件实体应当尽可能少的与其他实体发生相互作用。

    展开全文
  • 模式设计原则 ...面向接口编程 不将变量类型声明为具体的类,而是声明为某个接口。 客户程序不需知道对象的具体类型,只需知道对象所具有的接口。 封装变化点 将稳定点 和 变化点分离,扩...
  • 是什么 重复最少化,就是指极力消除重复。 许多技术都以实现重复最少化为目标,函数化技术就是其中之一,该技术将重复的逻辑函数化,整合成一段共享代码来使用。 为什么 通过复制、粘贴让同一段代码出现在多个...
  • 抽象不应该依赖细节,细节应该依赖抽象,其核心思想是:要面向接口编程,不要面向实现编程。 (4)接口隔离原则(Interface Segregation Principle, ISP) 定义:使用多个专门的接口,而不使用单一的总接口,即客户端...
  • “针对接口编程”的真正含义是“针对超类型编程”,它利用了多态的特性。 更明确的来说就是,一个变量 a 的声明类型应该是超类型A,所谓的超类型一般是抽象类或接口。超类型强调的是,它与它的所有派生类共有的...
  • 极限编程XP的关键实践(一)提到 XP 的关键实践,就不得不拿出下面这张图。看着眼熟不?是不是很多内容我们在上篇文章中其实都已经讲过了。没错,可能有些概念你很清楚,但有些概念你就完全没听说...
  • java--面向抽象编程

    2021-02-26 17:43:41
    所谓面向抽象编程是指当设计某种重要的类时,不让该类面向具体的类,而是面向抽象类,及所设计类中的重要数据是抽象类声明的对象,而不是具体类声明的对象。就是利用abstract来设计实现用户需求。比如:我们有一个...
  • PHP面向对象编程基本原则发布时间:2020-06-29 13:54:13来源:51CTO阅读:202作者:思梦教育首先祝大家节日快乐!!!额,不知道你们剁手没,小梦是没有!整整已经错过了第九个年头!小伙伴是不是有一种感觉,PHP...
  • 编程理论:三个思想、六个原则是什么指导编程的思想。在编程的过程中,人们最重视的莫过于编写出高质量的代码。高质量的代码是指拥有多种扩种的方法、不存在多余的要素、可读性高,易于理解的代码。编程...
  • C语言编程规范(个人整理)

    千次阅读 2021-05-02 12:22:00
    C语言编程规范(个人建议)前言1、基本命名规则2、排版规则3、编程规则4、使用宏定义打印Debug信息 前言   写这篇博客主要是希望自己将来能够按照这篇编程规范进行编程。 1、基本命名规则 规则: 文件名,函数,...
  • 偶然发现,在CSND上有这本书第一章的原文面向对象六大原则作者的博客哦,受教了。 这是文中例子的UML图,加入的我的理解。六大基本原则可分为:SOLID和迪米特原则。1.单一自责原则S——优化代码的第一步Single ...
  • 浅谈 C++ 元编程

    2021-06-14 00:39:08
    置顶/星标公众号????,硬核文章第一时间送达!随着 C++ 11/14/17 标准的不断更新,C++ 语言得到了极大的完善和补充。元编程作为一种新兴的编程方式,受到了越来越多的广泛关注。...
  • 编程原则

    2021-03-01 22:49:33
    本书介绍了软件领域非常有名的一些编程原则,这些原则能帮助我们编写优质的代码。编程的原则不是某种特定的技术,而是抽象度非常高的信息。 1. KISS原则 Keep It Simple, Stupid – 保持代码简洁. 2. DRY原则 Don...
  • 编程是一种与计算机系统通信的语言。交流就是与某人分享思想。二进制语言是计算机的最核心的语言。但是在前端,我们有很多种语言。这些语言使用解释器将前端代码转换为二进制代码。基本上,系统对一行代码执行大量...
  • 接口隔离原则(英语:interface-segregation principles, 缩写:ISP)指明客户(client)不应被迫使用对其而言无用的方法或功能。 接口隔离原则(ISP)拆分非常庞大臃肿的接口成为更小的和更具体的接口,这样客户将...
  • 1.单一职责原则(Single Responsibility Principle):一个类只做一类原则 2.开闭原则(Open-closed Principle):对扩展开放,对修改关闭,也就是在不修改源码的情况下改变对象的行为。 3.里氏替换原则(Liskov ...
  • 极限编程(XP)12个最佳实践 现场客户 ( On-site Customer ) 代码规范 ( Code Standards ) 每周40小时工作制 ( 40-hour Week ) 计划博弈 ( Planning Game ):要求结合项目进展和技术情况,确定下一阶段要开发与发布的...
  • 要面向接口编程,不要面向实现编程。抽象不依赖细节,细节不依赖抽象。 单一职责原则 控制类的粒度大小,将对象解耦、提高内聚性。也就是一个方法尽可能完成一件事。 5. ## 接口隔离原则 要为各个类建立它们所...
  • 面向对象编程

    2021-07-13 22:54:56
    1. 面向对象概念 ...识别真实对象的状态和行为是开始思考面向对象编程一种良好的方法。 软件对象在概念上与真实对象相似:它们也由状态和行为组成。对象将其状态存储在字段中,并通过方法公开其行为。方法对对象的
  • 比如说设计思想、设计原则、设计模式。之所以说重要,一方面是面试需要,找工作的时候,总需要跟面试官聊上几句,这样显得大家都够专业;另外一方面则这是通往高级工程师,架构师的必备基本技能。在这个系列中,前面...
  • 而声明式编程描述的是问题的定义,也就是当前问题的性质及解决问题时应满足的限制条件。 为什么 声明式的代码没有流程方面的限制。这种单纯阐述事实的表达方式能够提升代码的可读性。 另一方面。我们要想正确理解...
  • 你要用这个值进行什么操作(println) def f1(func: String=>Unit):Unit={ func("Alisa2") } f1(fun) f1((name:String)=>{println(name)}) //匿名函数的至简原则 //(1)参数的类型可以省略,会根据形参进行自动的推导...
  • 函数式编程 freecodecamp

    2021-01-20 15:07:44
    文章目录函数式编程:了解函数式编程术语函数式编程:了解使用命令式编程的危害函数式编程:使用函数式编程避免突变和副作用函数式编程:传递参数以避免函数中的外部依赖数式编程:在函数中重构全局变量函数式编程:...
  • 编程中的对称性要比图形中的对称性抽象的多,编程中的对称性是指相同思路的代码在任何地方都以相同的形式表现出来。 简单来说,就是组内的等级整理。同类的东西,也就是拥有相同性质的东西,要用相同的等级来表现。...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 387,967
精华内容 155,186
关键字:

编程原则