面向对象的六大原则
面向对象的六大原则
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,依赖倒置原则指代了一种特定的解耦形式,使得高层次的模块不依赖于低层次模块的实现细节的目的。依赖倒置原则有以下几个关键点:
- 高层模块不应该依赖底层模块,两者都应该依赖其抽象
- 抽象不应该依赖细节
- 细节应该依赖抽象
依赖倒置原则在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));
}}
【面向对象的六大原则】这样,租客只和中介通信,不会牵扯到具体的房子
推荐阅读
- 热闹中的孤独
- JAVA(抽象类与接口的区别&重载与重写&内存泄漏)
- 放屁有这三个特征的,请注意啦!这说明你的身体毒素太多
- 一个人的旅行,三亚
- 布丽吉特,人生绝对的赢家
- 慢慢的美丽
- 尽力
- 一个小故事,我的思考。
- 家乡的那条小河
- 《真与假的困惑》???|《真与假的困惑》??? ——致良知是一种伟大的力量