面向对象的六大原则

面向对象的六大原则

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 mRooms = new ArrayList<>();

    public Mediator() {
        for (int i = 0; i < 5; i++) {
            mRooms.add(new Room(14 + i, (14 + i) * 150));
        }
    }

    public List getAllRooms() {
        return mRooms;
    }
}



//租户
public class Tenant {
    public void rentRoom(float roomArea, float roomPrice, Mediator mediator) {
        List 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 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));
    }

}

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

你可能感兴趣的:(面向对象的六大原则)