1、面向对象7大原则
**1、开闭原则:**对扩展开放,对修改改变。
**2、里氏替换原则:**只要父类出现的地方,子类都可以替换他,使用者可能都不知道到底是子类还是父类(就是子类继承了超类,在子类中保持超类的特性)
**3、单一职责原则:**对类来说,就是一个类应该只负责一项职责。
**4、依赖倒置原则:**要面向接口编程,不要面向实现编程。
**5、接口隔离原则:**客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上。
**6、迪米特原则:**一个类对自己依赖的其他类知道的越少越好。
**7、合成复用原则:**尽量先使用组合或聚合等关联关系,其次才考虑使用继承关系来实现。
1.1 单一职责原则
对类来说,就是一个类应该只负责一项职责,这就是单一职责原则。
违反单一职责原则例子:
public class SingleReponsibility1 {
public static void main(String[] args) {
Vehicle vehicle = new Vehicle();
vehicle.run("汽车"); //输出: 汽车 在公路上跑
vehicle.run("摩托车");//输出: 摩托车 在公路上跑
vehicle.run("飞机");//输出: 飞机 在公路上跑
// 飞机的输出不符合常理,因为违反单一职责原则。
}
}
// 交通工具类
class Vehicle {
public void run(String vehicle){
System.out.println(vehicle + " 在公路上跑");
}
}
单一职责原则例子:
public class SingleReponsibility1 {
public static void main(String[] args) {
RoadVehicle roadVehicle = new RoadVehicle();
roadVehicle.run("汽车"); //输出: 汽车 在公路上跑
roadVehicle.run("摩托车");//输出: 摩托车 在公路上跑
AirVehicle airVehicle = new AirVehicle();
airVehicle.run("飞机");//输出: 飞机 在天上跑
}
}
// 路上交通工具类
class RoadVehicle {
public void run(String vehicle){
System.out.println(vehicle + " 在公路上跑");
}
}
//空中交通工具类
class AirVehicle {
public void run(String vehicle){
System.out.println(vehicle + " 在天上跑");
}
}
不过上面这个例子不是最好的,变成这个例子例子改动太大,最好是使用接口+多态的方式。
总结:
- 降低类的复杂度,一个类只负责一项职责。
- 提高类的可读性,可维护性。
- 减低变更引起的风险。
- 只有类的方法比较少,可以只在方法级别保持单一原则。
1.2 接口隔离原则
客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上。
违反接口隔离原则:
类B、D都实现了接口interface1,类A只需要使用到B的operation1和operation3方法,类C只需要用到D的operation2和operation4方法,这样就是 依赖了所不需要的接口了。
public class SegregationTest {
public static void main(String[] args) {
A a = new A();
a.dependOn1(new B());
a.dependOn3(new B());
C c = new C();
c.dependOn2(new D());
c.dependOn4(new D());
}
}
interface interface1{
void operation1();
void operation2();
void operation3();
void operation4();
}
class B implements interface1{ //B只被用到了operation1和operation3方法,却实现了所有接口方法。
@Override
public void operation1() {
System.out.println("B 实现了 operation1");
}
@Override
public void operation2() {
System.out.println("B 实现了 operation2");
}
@Override
public void operation3() {
System.out.println("B 实现了 operation3");
}
@Override
public void operation4() {
System.out.println("B 实现了 operation4");
}
}
class D implements interface1{ //D只被用到了operation2和operation4方法,却实现了所有接口。
@Override
public void operation1() {
System.out.println("D 实现了 operation1");
}
@Override
public void operation2() {
System.out.println("D 实现了 operation2");
}
@Override
public void operation3() {
System.out.println("D 实现了 operation3");
}
@Override
public void operation4() {
System.out.println("D 实现了 operation4");
}
}
class A{ //A只需要B的operation1和operation3方法
public void dependOn1(interface1 i){
i.operation1();
}
public void dependOn3(interface1 i){
i.operation3();
}
}
class C{ //只需要用到D的operation2和operation4方法
public void dependOn2(interface1 i){
i.operation2();
}
public void dependOn4(interface1 i){
i.operation4();
}
}
正确例子:
进行接口分离。
public class SegregationTest {
public static void main(String[] args) {
A a = new A();
a.dependOn1(new B());
a.dependOn3(new B());
C c = new C();
c.dependOn2(new D());
c.dependOn4(new D());
}
}
interface interface1{
void operation1();
void operation3();
}
interface interface2{
void operation2();
void operation4();
}
class B implements interface1{
@Override
public void operation1() {
System.out.println("B 实现了 operation1");
}
@Override
public void operation3() {
System.out.println("B 实现了 operation3");
}
}
class D implements interface2{
@Override
public void operation2() {
System.out.println("D 实现了 operation2");
}
@Override
public void operation4() {
System.out.println("D 实现了 operation4");
}
}
class A{
public void dependOn1(interface1 i){
i.operation1();
}
public void dependOn3(interface1 i){
i.operation3();
}
}
class C{
public void dependOn2(interface2 i){
i.operation2();
}
public void dependOn4(interface2 i){
i.operation4();
}
}
1.3 依赖倒置原则
依赖倒置原则:
- 高层模块不应该依赖底层模块,二者都应该依赖其抽象。(高层理解为吃动作,底层理解为面包等具体食物)
- 抽象不应该依赖细节,细节应该依赖抽象。
- 依赖倒置的中心思想就是面向接口编程。
在继承时要遵循里氏替换原则。
错误示例:
class DependencyInversionTest {
public static void main(String[] args) {
Person person = new Person();
person.receive(new Email());
}
}
//电子邮箱类
class Email{
public String getInfo(){
return "获得电子邮件信息";
}
}
//完成Person接受消息的功能
//缺点:如果要接受消息的对象是 微信类、短信类,就得增加相应的方法。
class Person{
public void receive(Email email){
System.out.println(email.getInfo());
}
}
解决示例:
引入一个抽象的接口IReceiver,表示接收者,这样Person类就只与接口发送依赖
class DependencyInversionTest {
public static void main(String[] args) {
Person person = new Person();
person.receive(new Email());
person.receive(new Wechat());
}
}
interface IReceiver{
String getInfo();
}
//电子邮箱类
class Email implements IReceiver{
public String getInfo(){
return "获得电子邮件信息";
}
}
//微信类
class Wechat implements IReceiver{
@Override
public String getInfo() {
return "获得微信信息";
}
}
//完成Person接受消息的功能
class Person{
//这样就不需要每添加一个消息对象就写相应的方法
public void receive(IReceiver receiver){
System.out.println(receiver.getInfo());
}
}
上面的示例是基于接口实现的,下面的示例是基于构造函数来实现的:
class DependencyInversionTest {
public static void main(String[] args) {
Person person = new Person(new Email());
person.receive();
person = new Person(new Wechat());
person.receive();
}
}
interface IReceiver{
String getInfo();
}
//电子邮箱类
class Email implements IReceiver{
public String getInfo(){
return "获得电子邮件信息";
}
}
//微信类
class Wechat implements IReceiver{
@Override
public String getInfo() {
return "获得微信信息";
}
}
//完成Person接受消息的功能
class Person{
private IReceiver iReceiver;
public Person(IReceiver iReceiver) {
this.iReceiver = iReceiver;
}
public void receive(){
System.out.println(iReceiver.getInfo());
}
}
下面的示例是基于setter方法来实现:
class DependencyInversionTest {
public static void main(String[] args) {
Person person = new Person();
person.setiReceiver(new Email());
person.receive();
person.setiReceiver(new Wechat());
person.receive();
}
}
interface IReceiver{
String getInfo();
}
//电子邮箱类
class Email implements IReceiver{
public String getInfo(){
return "获得电子邮件信息";
}
}
//微信类
class Wechat implements IReceiver{
@Override
public String getInfo() {
return "获得微信信息";
}
}
//完成Person接受消息的功能
class Person{
private IReceiver iReceiver;
public void setiReceiver(IReceiver iReceiver) {
this.iReceiver = iReceiver;
}
public void receive(){
System.out.println(iReceiver.getInfo());
}
}
1.4 里氏替换原则
**里氏替换原则:**所有引用基类(父类)的地方必须能透明地使用其子类的对象。也就是说子类可以扩展父类的功能,但不能改变父类原有功能。
透明使用的关键就是,子类不能改变父类原有功能。
错误示例:子类改变了父类原有功能后,当我们在引用父类的地方使用其子类的时候,没办法透明使用add方法了。
public class Test53 {
public static void main(String[] args) {
//当我们在引用父类的地方使用其子类的时候,无法透明的使用add方法
Person person = new Children();
person.add();
}
}
class Person{
public void add(){
System.out.println("父类");
}
}
class Children extends Person{
public void add(){
System.out.println("子类");
}
}
正确示例:将原来的父类和子类都继承一个更通俗的基类(抽象类)。
public class Test53 {
public static void main(String[] args) {
// Person person = new Children(); 这个用不了
All all = new Person();
all.add();
all = new Children();
all.add();
}
}
abstract class All{
public abstract void add();
}
class Person extends All{
public void add(){
System.out.println("父类");
}
}
class Children extends All{
public void add(){
System.out.println("子类");
}
}
1.5 开闭原则
**开闭原则:**对扩展开发,对修改关闭。
错误示例:
public class OcpTest {
public static void main(String[] args) {
GraphicEditor graphicEditor = new GraphicEditor();
graphicEditor.drawShape(new Retangle());
graphicEditor.drawShape(new Circle());
}
}
//绘制图形的类
class GraphicEditor{
//当我再加一个图形时,这个方法就必须改动。不符合对修改关闭。
public void drawShape(Shape shape){
if(shape.type==1){
drawRetangle();
}else if(shape.type==2){
drawCircle();
}
}
public void drawRetangle(){
System.out.println("绘制矩形");
}
public void drawCircle(){
System.out.println("绘制圆形");
}
}
//图形类
class Shape {
int type;
}
//矩形类
class Retangle extends Shape {
public Retangle() {
super.type = 1;
}
}
//圆形类
class Circle extends Shape {
public Circle() {
super.type = 2;
}
}
正确示例:使用抽象类。
public class OcpTest {
public static void main(String[] args) {
GraphicEditor graphicEditor = new GraphicEditor();
graphicEditor.drawShape(new Retangle());
graphicEditor.drawShape(new Circle());
}
}
//绘制图形的类
class GraphicEditor{
public void drawShape(Shape shape){
shape.draw();
}
}
//图形类
abstract class Shape {
abstract public void draw();
}
//矩形类
class Retangle extends Shape {
@Override
public void draw() {
System.out.println("绘制矩形");
}
}
//圆形类
class Circle extends Shape {
@Override
public void draw() {
System.out.println("绘制圆形");
}
}
1.6 迪米特法则
**迪米特法则(又称最小知道法则):**一个类对自己依赖的其他类知道的越少越好。
一个对象应该对其他对象有最少的了解要满足这两点:
- 只和直接朋友交流。
- 减少对朋友的了解。
**直接朋友:**出现在成员变量、方法的返回类型,方法参数就是直接的朋友。(只出现在方法体内部的类不是直接朋友)
**迪米特法则的目的:**减低类之间的耦合度,但并不是要求完全没有依赖关系。
错误示例:
public class DemeterTest {
public static void main(String[] args) {
Person person = new Person();
person.washClothes();
}
}
//洗衣机类
class WashingMachine{
public void receiveClothes(){
System.out.println("洗衣街接收到了衣服");
}
public void wash(){
System.out.println("洗衣机洗衣服");
}
public void dry(){
System.out.println("洗衣机烘干衣服");
}
}
//人
class Person{
//让洗衣机洗衣服的方法
public void washClothes(){
//由人来指定洗衣机洗衣服的顺序,Person类对WashingMachine类知道的太多。
WashingMachine washingMachine = new WashingMachine();
washingMachine.receiveClothes();
washingMachine.wash();
washingMachine.dry();
}
}
正确示例:
public class DemeterTest {
public static void main(String[] args) {
Person person = new Person();
person.washClothes(new WashingMachine());
}
}
//洗衣机类
class WashingMachine{
public void receiveClothes(){
System.out.println("洗衣街接收到了衣服");
}
public void wash(){
System.out.println("洗衣机洗衣服");
}
public void dry(){
System.out.println("洗衣机烘干衣服");
}
public void autoWash(){
receiveClothes();
wash();
dry();
}
}
//人
class Person{
//让洗衣机洗衣服的方法
public void washClothes(WashingMachine washingMachine){
washingMachine.autoWash();
}
}
1.7 合成复用原则
原则是尽量使用合成/聚合的方式。而不是使用继承。