美文网首页
Java面向对象

Java面向对象

作者: 耳_总 | 来源:发表于2017-07-14 14:53 被阅读4次
    说说你读面向对象的理解?

    在Java面万事万物皆为对象。面向对象就是把一个对象抽象成具体的类,我们需要它来实现什么功能,它有什么特性,需要做什么事情,然后把这些特性和功能抽象成类的属性和方法。一个类代可能是一个具体事务的抽象,也可能对某一类功能的封装。
    站在类的层面来说,可以利用封装、继承、多态等特性来功能的制定规范、扩展、类的复用等等。类与类之间可以恰当的用设计模式和遵循一些设计原则来进行类之间的继承、组合,达到可复用性、可扩展性、可维护性等目的。


    六大设计原则
    • 里氏置换原则
      只要父类出现的地方子类就能够出现,而且替换为子类不会产生任何错误或异常。这个原则在策略模式中就有很好的体现,父类通常是一个接口,统一算法簇的行为,子类做具体实现,场景类在引用的时候传入的参数是接口,而不是其实现类,用的是算法簇父类的引用,指向子类的实现,这里也体现了多态。总结起来就是父类的引用指向子类的对象。

    • 依赖倒置原则
      首先它是一种类的依赖关系(局部变量、方法参数、静态方法的调用)
      而依赖倒置原则的本质其实就是通过抽象(抽象类或接口)使各个类或模块的实现彼此独立,不相互影响,实现模块间的松耦合,其实就是面向接口编程,各个模块之间的子类都是依赖各个模块的父类接口,而不是实现类,这样子达到松耦合。
      1、 高层模块不依赖底层模块,两者都应该依赖抽象(抽象类或接口)
      2、抽象不依赖具体实现类
      3、具体实现类依赖抽象
      这就是面向接口编程(OOD)的精髓之一
      列子:

    public class Luffy {
        //luffy吃苹果
        public void eat(Apple apple) {
            apple.eat();
        }
    }
    public class Apple {
        //只有吃苹果
        public void eat() {
            System.out.println("吃苹果");
        }
    }
    //测试类
    public class Test {
    
        public static void main(String[] args) {
            Luffy luffy = new Luffy();
            Apple apple = new Apple();
            luffy.eat(apple);
        }
    }
    

    这样子设计的话Luffy只会吃苹果,因为eat()的类型限定死了,显然这是不科学的,Luffy还需要吃梨子、香蕉等等。可以看到这里的Luffy和Apple两个实现类形成了依赖关系,这显然违背了我们设计原则,可扩性差,也不符合实际情况。改进:
    让Luffy实现 IPerson接口,让Apple实现IFood接口:

    public interface IPerson {
        void eat(IFood food);
    }
    
    public interface IFood {
        void eat();
    }
    
    public class Luffy implements IPerson{
        @Override
        public void eat(IFood food) {
            // TODO Auto-generated method stub
            food.eat();
        }
    }
    
    public class Apple implements IFood{
        //只有吃苹果
        public void eat() {
            System.out.println("吃苹果");
        }
    }
    public class Test {
    
        public static void main(String[] args) {
            Luffy luffy = new Luffy();
            Apple apple = new Apple();
    //      Banner banner = new Banner();
            luffy.eat(apple);
    //      luffy.eat(banner);
        }
    }
    

    这样子让抽象IPerson依赖IFood,具体实现交给Luffy和Apple、Banner。通过抽象不会造成Luffy类和Apple等类的依赖关系,松耦合,还可以扩展Jason吃梨子等等功能。

    2.png

    可以看到在改进之前类之间有依赖关系,改进后通过接口进行了松耦合。依赖的只是高层抽象接口。

    • 接口隔离原则
      接口隔离原则比较简单,就是定义的一个接口按照需求和其责任细分,不要把太多的抽象定义在一个接口。
      1、接口尽量小,不能出现臃肿的接口,在进行拆分接口的同时,一定要满足单一职责原则
      2、接口要高内聚,尽量少的对外公布public方法
      3、一个接口只负责一个子模块的逻辑
      但是在实际开发过程中要根据开发经验和实际情况设计接口的粒度,不能完全按照接口隔离原则,粒度越小,接口类越多,越难维护。粒度越大,灵活性降低。

    • 单一职责原则
      定义:应该仅有一个原因引起类的变更,也就是一个类只有一个职责。
      在我们在设计接口的时候,可能一个接口定义了好几种行为,这样的话一个接口就承担了多种职责,职责不明确,比如:

    public interface ILuffy {
        String getName();   
        void eat();
    }
    

    一个接口定义了获取人名(属性)的职责,还定义了吃这个行为,所以一接口承担了两种职责。可以进行如下改进:

    public interface IAttrs {
        void setAge(int age);
    }
    public interface IAction {
        void eat();
    }
    

    上面的例子在于年龄、名字等等属于属性,吃东西属于行为,需要不同的接口来承担不同的责任。
    解决办法:将不同的职责分为不同的接口即可。
    那么单一职责原则的意义何在呢?
    降低类的复杂性,实现什么样的职责都有清晰的定义
    提高可读性
    提高可维护性
    降低变更引起的风险,对系统扩展性和维护性很有帮助
    但是、使用单一职责原则有一个问题,“职责”没有一个明确的划分标准,如果把职责划分的太细的话会导致接口和实现类的数量剧增,反而提高了复杂度,降低了代码的可维护性。所以使用这个职责的时候还要具体情况具体分析。建议就是接口一定要采用单一职责原则,实现类的设计上尽可能做到单一职责原则,最好是一个原因引起一个类的变化。
    单一职责不仅仅用在类中,也同样适用方法。

    • 迪米特法则
      也称最少知识原则。一个类应该对他耦合或调用的类知道的最少。
      迪米特法则有一个原则就是“只和朋友类交流”,什么是朋友类呢?每个对象都有与其相耦合的类,两个对象之间的耦合被称为朋友关系,这种关系有很多比如:聚合、组合、依赖。
      有这么个故事:体育老师让体育委员确认班上的女生到齐了没有,对他说:“你去清下班上女生”,体育委员:“亲....亲那个(羞羞)?”
    3.png
    public class Teacher {
        
        public void commond(GroupLeader groupLeader) {
            ArrayList<Gril> arrayList = new ArrayList<>();
            arrayList.add(new Gril());
            arrayList.add(new Gril());
            arrayList.add(new Gril());
            groupLeader.countGrils(arrayList);
        }
    }
    
    public class GroupLeader {
    
        public void countGrils(List<Gril> grils) {
            System.out.println("女生的人数:" + grils.size());
        }
    }
    
    public class Gril {
    }
    
    public static void main(String[] args) {
            Teacher teacher = new Teacher();
            teacher.commond();
        }
    
    • 定义了一个Teacher类和一个commond发送指令的方法,方法里面实例了一个GroupLeader对象,然后定义了一个Grils数组,让GroupLeader去清点人数。最后的结果:女生的人数:3。
      虽然说目的是达到了,但是这里的设计是有问题的。Teacher对象只有一个朋友类——GroupLeader,有人会认为Gril不是吗?不是在方法里面有调用,然后形成了依赖关系。但是按照定义:只有一个对象是另一个对象的成员变量或者作为方法参数传入的才能称为是朋友类。在这里Gril并不是,但是有出现在commond方法体里面和Gril类有了交流。迪米特法则告诉我们只与朋友里交流,这里违背了这一原则。
      这样就破坏了Teacher类的健壮性。方法是一个类的行为,在方法里面竟然不知道与另外一个类的依赖关系,这个严重违反了迪米特法则。
      经过改进后的类图:
    4.png
    public class Teacher {
        public void commond(GroupLeader leader) {
            leader.countGrils();
        }
    }
    public class GroupLeader {
        private List<Girl> grils;
    
        public GroupLeader(List<Girl> grils) {
            this.grils = grils;
        }
    
        public void countGirls() {
            System.out.println("女生的人数:" + grils.size());
        }
    }
    public static void main(String[] args) {
            Teacher teacher = new Teacher();
            ArrayList<Gril> arrayList = new ArrayList<>();
            arrayList.add(new Gril());
            arrayList.add(new Gril());
            arrayList.add(new Gril());
            GroupLeader groupLeader = new GroupLeader(arrayList);
            teacher.commond(groupLeader);
        }
    

    这样把Gril类的初始化放在了场景类中,在GroupLeader中增加了对Girl类的注入,避开了Teacher对Girl的访问,降低了耦合。
    迪米特法则的核心就是实现类之间的解耦,弱耦合,只有弱耦合之后,类的复用率才可以提高。其结果就是会产生大量的中间类,增加了系统的复杂性,和维护难度

    • 开闭原则
      开闭原则算是前5中原则的一个抽象总结,没有一个固定的模式,但是最终保证的是提高程序的复用性、可维护性等要求。
      对扩展开放,对修改关闭。意思是我们需要扩展一个功能的时候是通过扩展接口来实现,而不是通过修改源码。

    相关文章

      网友评论

          本文标题:Java面向对象

          本文链接:https://www.haomeiwen.com/subject/qmzvhxtx.html