美文网首页
面向对象的六大原则

面向对象的六大原则

作者: Yolyn | 来源:发表于2018-12-28 14:18 被阅读40次

    面向对象的六大原则

    1.单一职责原则(Single Responsibility Principle)

    单一职责原则缩写SRP,定义为:就一个类而言,应该仅有一个引起它变化的原因,简单来说,一个类中应该是一组相关性很高的函数、数据的封装,但单一职责的划分界限总是不那么清晰,很多时候都是需要靠个人经验来界定的,当然,最大的问题是对职责的定义,什么是类的职责,以及怎么划分类的职责。

    如何划分一个类、一个函数的职责,每个人都有自己的看法,这需要根据个人经验、具体的业务逻辑而定,但是,它也有一些基本的指导原则,例如,两个完全不一样的功能就不应该放在一个类中。一个类中应该是一组相关性很高的函数、数据的封装。工程师可以不断地审视自己的代码,根据具体的业务、功能对类进行相应拆分,这是程序员优化代码的第一步。

    2. 让程序更稳定、更灵活的开闭原则(Open Close Principle)

    开闭原则缩写OCP,它是java世界里最基础的设计原则,它知道我们如何建立一个稳定、灵活的系统。开闭原则的定义是:软件中的对象(类、模块、函数等)应该对于扩展是开放的,但是,对于修改应是封闭的。在软件的生命周期中,因为变化、升级和维护等原因需要对原有代码进行修改时,可能会将错误引入原本已经经过测试的旧代码中,破坏原有系统。因此,当软件需要变化时我们应该尽量通过扩展的方式来实现变化,而不是通过修改已有代码来实现。

    当然在现实开发中,只通过继承的方式来升级、维护原有系统只是理想化的愿景,因此,在实际开发中,修改原有代码和扩展代码往往是同时存在的。

    3. 构建扩展性更好的系统,里氏替换原则(Liskov Substitution Principle)

    里氏替换原则缩写LSP,它的定义是:如果对每一个类型为S的对象O1,都有类型T的对象O2,使得T定义的所有程序P在所有的对象O1都替换为O2时,程序P的行为没有发生变化,那么类型S是类型T的子类型。

    第二种定义是:所有引用基类的地方必须能透明地使用子类的对象,通俗的讲只要父类能出现的地方子类就可以出现,而且替换为子类也不会产生任何错误和异常,而反过来就不行了。———多态

    public class Window{
        public void show(View child){
            child.draw()
        }
    }
    
    public abstract class View{
        public abstract void draw();
        public void measure(int width,int height){
            //测量视图的大小
        }
    }
    
    public class TextView extends View{
        public void draw(){
            //绘制文本
        }
    }
    
    public class ImageView extends View{
        public void draw(){
            //绘制图片
        }
    }
    

    Window依赖于View,而View定义了一个视图抽象,measure是各个子类共享的方法,子类通过覆写View的draw方法实现具有各种特色的功能,任何继承自View类的子类都可以传递给show函数,这就是所说的里氏替换原则,就可以定义各种各样的View然后传递给Window,Window负责组织View,将View显示到屏幕上。

    4. 让项目拥有变化的能力,依赖倒置原则(Dependence Inversion Principle)

    依赖倒置原则缩写DIP,依赖倒置原则指代了一种特定的解耦形式,使得高层次的模块不依赖于低层次模块的实现细节的目的。依赖倒置原则有以下几个关键点:

    1. 高层模块不应该依赖底层模块,两者都应该依赖其抽象
    2. 抽象不应该依赖细节
    3. 细节应该依赖抽象

    在java中抽象指的是抽象类或者接口,细节这的是实现接口或者继承抽象类而产生的类,其特点是可以实例化。高层模块就是调用端,底层模块就是具体实现类。

    依赖倒置原则在java语言中的表现是:模块之间依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的,一句话概括就是面向接口编程或者说是面向抽象编程。

    package DIPDemo;
    /**
     * 
     * @author houyl
      *  依赖倒置原则
     */
    interface IPerson {
    void work();
    void eat();
    }
    
    
    
    public class NormalPerson implements IPerson{
    
        @Override
        public void work() {
            // TODO Auto-generated method stub
            System.out.println("做普通的工作");
        }
    
        @Override
        public void eat() {
            // TODO Auto-generated method stub
            System.out.println("吃快餐");
        }
    
    }
    
    
    public class Student implements IPerson{
    
        @Override
        public void work() {
            // TODO Auto-generated method stub
            System.out.println("工作就是学习");
        }
    
        @Override
        public void eat() {
            // TODO Auto-generated method stub
            System.out.println("吃食堂");
        }
    
    }
    
    
    
    
    public class Teacher implements IPerson{
    
        @Override
        public void work() {
            // TODO Auto-generated method stub
            System.out.println("教书");
        }
    
        @Override
        public void eat() {
            // TODO Auto-generated method stub
        System.out.println("吃教职工食堂");
        }
    
    }
    
    
    
    
    public class TheirLife {
        IPerson person = new NormalPerson();//只有一个默认实现
    //Teacher t=new Teacher();//依赖于细节
        public void work() {
            person.work();
        }
    
        public void eat() {
            person.eat();
        }
       //依赖于抽象
        public void setPerson(IPerson person) {
            this.person = person;
        }
    
    }
    
    
    public class Test {
    public static void main(String[] args) {
        IPerson p=new Teacher();
        TheirLife tl=new TheirLife();
        tl.setPerson(p);
        tl.work();
        tl.eat();
    }
    }
    

    5. 系统有更高的灵活性,接口隔离原则(InterfaceSegregation Principle)

    接口隔离原则缩写ISP,它的定义是,客户端不应该依赖于它不需要的接口,另一种定义是:类间的依赖关系应该建立在最小的接口上,接口隔离原则将非常庞大、臃肿的接口拆分成更小的和更具体的接口,这样客户将会只需要知道他们感兴趣的方法,接口隔离原则目的是系统解开耦合,从而容易重构、更改和重新部署。

    例如:

    public class CloseUtils {
    public static void closeQuitely(Closeable closeable) {
        if(null!=closeable) {
            try {
                closeable.close();
            }catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    }
    
    
    public class Test {
    public static void main(String[] args) {
        
        File f =new File("a.txt");
        FileOutputStream fos=null;
        try {
            fos = new FileOutputStream(f);
            fos.write("你好".getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            CloseUtils.closeQuitely(fos);
        }
    
    }
    

    建立一个类专门关闭IO流对象,因为很多IO相关的对象都实现了这个Closeable接口。CloseUtils的closeQuietly方法的基本原理就是依赖于Closesable抽象而不是具体实现(依赖倒置原则),并且建立在最小化依赖的原则的基础上,它只需知道这个对象是可关闭的,其他的一概不关心,也就是接口隔离原则。

    6. 更好的可扩展性,迪米特原则(Law of Demeter)

    迪米特原则缩写LOD,也称为最少知识原则,虽然名字不同,但描述的是同一个原则,即:一个对象应该对其他对象有最少的了解。通俗地讲,一个类应该对自己需要耦合或者调用的类知道的最少,类的内部如何实现与调用者或者依赖者没关系,调用者或者依赖者只需知道它需要的方法即可,其他的可一概不管。类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类影响也越大。

    看下面的代码:

    package LODDemo;
    
    public class Room {
        public float area;
        public float price;
    
        public Room(float area, float price) {
            this.area = area;
            this.price = price;
        }
    
        @Override
        public String toString() {
            return "Room [area=" + area + ", price=" + price + "]";
        }
    
    }
    
    
    //中介
    public class Mediator {
        List<Room> mRooms = new ArrayList<>();
    
        public Mediator() {
            for (int i = 0; i < 5; i++) {
                mRooms.add(new Room(14 + i, (14 + i) * 150));
            }
        }
    
        public List<Room> getAllRooms() {
            return mRooms;
        }
    }
    
    
    
    //租户
    public class Tenant {
        public void rentRoom(float roomArea, float roomPrice, Mediator mediator) {
            List<Room> rooms = mediator.getAllRooms();
            for (Room room : rooms) {
                if (isSuitble(roomArea, roomPrice, room)) {
                    System.out.println("租到房了" + room);
                    break;
                }
            }
        }
    
        private boolean isSuitble(float roomArea, float roomPrice, Room room) {
            // TODO Auto-generated method stub
            return roomArea >= room.area && roomPrice <= roomPrice;
        }
    }
    
    

    上面代码中租户不仅依赖了中介还频繁地与Room类打交道,租户只是通过中介找到一间适合自己的房子,如果把这些检测条件都放在租客上,就会弱化中介的功能,而且导致租客和Room的耦合度增加。
    优化后:

    public class Room {
        public float area;
        public float price;
    
        public Room(float area, float price) {
            this.area = area;
            this.price = price;
        }
    
        @Override
        public String toString() {
            return "Room [area=" + area + ", price=" + price + "]";
        }
    
    }
    
    
    
    public class Mediator {
        List<Room> mRooms = new ArrayList<>();
    
        public Mediator() {
            for (int i = 0; i < 5; i++) {
                mRooms.add(new Room(14 + i, (14 + i) * 150));
            }
        }
    
        public Room rentOut(float area, float price) {
            for (Room room : mRooms) {
                if (isSuitble(area, price, room)) {
                    return room;
                }
            }
            return null;
        }
    
        private boolean isSuitble(float roomArea, float roomPrice, Room room) {
            // TODO Auto-generated method stub
            return roomArea >= room.area && roomPrice <= roomPrice;
        }
    }
    
    
    
    public class Tenant {
        public void rentRoom(float roomArea, float roomPrice, Mediator mediator) {
            System.out.println("租到房了" + mediator.rentOut(roomArea, roomPrice));
        }
    
    }
    
    

    这样,租客只和中介通信,不会牵扯到具体的房子

    相关文章

      网友评论

          本文标题:面向对象的六大原则

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