Java面向对象编程原则

本文详细介绍了面向对象编程的七大原则,包括单一职责原则、开闭原则、里氏替换原则、依赖倒置原则、接口隔离原则、合成复用原则和迪米特法则。这些原则旨在提高软件的可维护性、可复用性和可扩展性,通过实例分析展示了如何在实际编程中应用这些原则,以优化代码结构和设计。
摘要由CSDN通过智能技术生成

面向对象编程原则概述

1.1 软件的可维护性和可复用性

可维护性较低的软件设计,通常由于如下四个原因造成:

  • 过于僵硬(Rigidity) :灵活性不够
  • 过于脆弱(Fragility) :健壮性(鲁棒性)
  • 不够复用率低(Immobility) :不能重用
  • 黏度过高(Viscosity) :高耦合,关联性太高

一个好的系统设计应该具备如下三个性质:

  • 可扩展性(Extensibility)
  • 灵活性(Flexibility)
  • 可插入性(Pluggability)

程序设计原则:高内聚,低耦合

面向对象编程的七大原则

注意:这七个原则并非是孤立的。这7个原则是一个整体。

单一职责原则

定义:

单一职责原则(Single Responsibility Principle, SRP)定义如下:在软件系统中,一个类只负责一个功能领域中的相应职责。

分析:

​ 一个类(或者大到模块,小到方法)承担的职责越多,它被复用的可能性越小。而且如果一个类承担的职责过多,就相当于将这些职责耦合在一起,当其中一个职责变化时,可能会影响其他职责的运作。
​ 类的职责主要包括两个方面:数据职责和行为职责,数据职责通过其属性来体现,而行为职责通过其方法来体现。
​ 单一职责原则是实现高内聚√低柄甘h要设计人员发现类的不同职贡并将其分离,而发存在,它是最简单但又最难运用的原则,需要设计人员发现类的不同职责并将其分离,而发现类的多重职责需要设计人员具有较强的分析设计能力和相关重构经验。

示例:

​ 编写一个程序:实现两个数+,-,*,/运算,并输出结果

程序1:

public class Cal {
	public static void main(String[] args) {
		Scanner scan = new Scanner(System.in);
   		System.out.print("输入num1 : ");
		double num1 = scan.nextDouble();
        System.out.print("输入运算符:");
        String oper = scan.next();
		System.out.print("输入Num2: ");
        double num2 = scan.nextDouble();
        
		if(oper.equals("+")){         
			System.out.println(num1+num2);
        }else if(oper.equals("-")){
			System.out.println(num1-num2);
        }else if(oper.equals("*")){
			System.out.println(num1*num2);
        }else if(oper.equals("/")){
            System.out.println(num1/num2);
        }
    }
}


程序分析:

​ 1、这个方法的职责有:获取数据(从控制台),判断运算符,执行计算,输出结果。
​ 2、这个万法巴d田加法运算。这时,上面的程序就需要去修改整个程序。这就会有发一个计算器,需要使用加法运算。这时,上面的程序完全无法使用。(复用率低下)
​ 3、如果要修改某个计算或者要增加某个计算,那么就需要去修改整个程序。这就会有程序修改的风险。

程序优化:

​ 1、先考虑程序的复用率,使用单一职责原则来进行优化。
​ 2、获取数据和输出结果与程序开发的平台有关。(可以将这两个功能放到一个类中3、具体的计算,可以每一个计算都进行封装。
​ 4、判断运算符(程序逻辑),可以进行封装.优化后的类图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dnE1n0E0-1633762813697)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20201202222928641.png)]

优化后的程序:

//负责加法运算
public class Add {
    public double cal( double num1, double num2){
   		 return num1+num2;
	}
}
//负责减法运算
public class Sub {
    public double cal( double num1, double num2){
   		 return num1-num2;
	}
}
//负责判断运算符,并调用具体的运算类
public class Cal{
    public double oper( String oper , double num1, double num2){
        double rst = 0;
        if(oper.equals("+")){
            Add add = new Add();
            rst = add.cal(num1,num2) ;
        }else if(oper.equals("-")){
            sub sub = new Sub();
            rst = sub.cal( num1, num2);
        }
        return rst;
    }
}
//负责数据的收集与显示
public class MainTest{
    public static void main(String[] args) i
        //收集数据
        Scanner scan = new Scanner(System.in) ;
        System.out.print("输入num1: ");
        double num1 = scan.nextDouble();
        System.out.print("输入运算符:");
        String oper = scan.next();
        system.out.print("输入Num2: ");
        double num2 = scan.nextDouble();
        //调用cal类得到计算结果
        cal cal = new Cal();
        double rst = cal.oper(oper,num1,num2);
        //输出结果
    	System.out.println(rst)
	}
}
优化后的程序分析:

1、整体的计算功能可移植(可复用)。将具体的业务逻辑和显示平台进行分离。业务逻辑和具体的展示无关。于是这个逻辑就可以使用到任何的展示平台中。
2、具体的计算进行了分离。每一个计算都可复用。
3、分类了具体的计算,那么如果要对其中某一个计算进行修改,就不会影响到其他的计算对象。(提高了程序的可维护性)

使用单一职责的好处

  1. 类的复杂性降低,实现什么职责都有清晰明确的定义2.可读性提高,复杂性降低,那当然可读性提高了
  2. 可维护性提高,那当然了,可读性提高,那当然更容易维护了
  3. 变更引起的风险降低,变更是必不可少的,如果接口的单一职责做得好,一个接口修改
  4. 只对相应的实现类有影响,对其他的接口无影响,这对系统的扩展性、维护性都有非常大帮助
  5. 注意:
    单一职责的应用应该要有一个合理的限度,并不是越单一越好。如果类的功能越单一,那么就会产生越多的类,类的数量越多就会造成整个系统的结构越复杂。所以我们在设计系统的时候需要找到一个合理的平衡点。

开放封闭原则

开闭原则定义

一个软件实体应当对扩展开放,对修改关闭。也就是说在设计一个模块的时候,应当使这个模块可以在不被修改的前提下被扩展,即实现在不修改源代码的情况下改变这个模块的行为
1、对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。
2、对修改封闭,意味着类一旦设计完成,就可以独立完成其工作,而不要对其进行任何的修改。

如何实现开放封闭原则

实现开闭原则的核心思想就是对抽象编程,而不对具体编程,因为抽象相对稳定。让类依赖于固定的抽象,所以修改就是封闭的;而通过面向对象的继承和多态机制,又可以实现对抽象类的继承,通过覆写其方法来改变固有行为,实现新的拓展方法,所以就是开放的。

软件设计要容易维护又不容易出问题的最好方法就是多扩展、少修改

使用开闭原则优化计算机项目

//负责判断运算符,并调用具体的运算类
public class Cal{
    
    public double oper( String oper , double num1, double num2){
        double rst = 0;
        if(oper.equals("+")){
            Add add = new Add();
            rst = add.cal(num1,num2) ;
        }else if(oper.equals("-")){
            sub sub = new Sub();
            rst = sub.cal( num1, num2);
        }
        return rst;
    }
}

分析:

1、在上面的代码中,计算的种类可能增加,可能减少。如果要增加一个乘法类,那么就必须要修改oper方法。而且这中修改在目前的程序结构下是无法避免的。

2、现在要变更的最有可能的地方是:每一个计算类(增加,减少,修改)

3、对计算方法进行抽象,然后在oper类中使用多态来封闭这种变化

优化后的程序:

//定义一个计算接口
public interface Cal {
    public double cal(double num1,double num2);
}
//加法实现类
public class Add implements Cal {
    @Override
    public double cal(double num1, double num2) {
        return num1+num2;
    }
}
//减法实现类
public class Sub implements Cal {
    @Override
    public double cal(double num1, double num2) {
        return num1-num2;
    }
}
//工厂类
public class Factory {
    public static Cal createInstance(String oper){
        Cal cal = null;
        switch (oper){
            case "+":
                cal = new Add();
                break;
            case "-":
                cal = new Add();
                break;
        }
        return cal;
    }
}
public class MainTest {
    public static void main(String[] args) {
        //收集数据
        Scanner input = new Scanner(System.in);
        System.out.println("输入num1");
        double num1 = input.nextDouble();
        System.out.println("输入运算符");
        String oper = input.next();
        System.out.println("输入num2");
        double num2 = input.nextDouble();

        //调用工厂类的方法获取具体的计算实例
        Cal cal = Factory.createInstance(oper);
        double result = cal.cal(num1, num2);
        //输出结果
        System.out.println(result);
    }
}

程序分析:
1、上面的程序中,如果要增加一个乘法计算。需要增加一乘法类,然后修改Factory类。
2、在factory类中使用了多态特性。客户端只需要向Factory类发起请求,Factory类会向客户端返回一个计算对象。(客户端不用去管这个计算对象是怎么来的>
3、在这个程序中,Factory中的判断修改没有封闭掉。但是可以使用反射和配置文件的方式将这种变化放到配置文件中。(将变化放到配置文件中,好处在于:配置文件是可以随时修改的,而程序修改后需要重新编译和发布)

oper.properties配置文件:

+=com.zsn.test.Add
-=com.zsn.test.sub
//工厂类
public class Factory {
    public static Cal createInstance(String oper) {
        Cal cal = null;
        try {
            //加载配置文件
            Properties properties = new Properties();
            properties.load(Factory.class.getClassLoader().getResourceAsStream("oper.properties"));
            //获取需要执行类的全类名
            String clz = properties.getProperty(oper);
            System.out.println(clz);
            //动态加载
            Class clazz = Class.forName(clz);
            //创建实例
             cal = (Cal)clazz.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return cal;
    }
}

4、上面这种程序结构称为:简单工厂模式

依赖倒置原则

依赖倒置原则的定义

依赖倒置原则的核心思想是:依赖于抽象。具体而言就是高层模块不依赖于底层模块,二者都同依赖于抽象;抽象不依赖于具体,具体依赖于抽象。

依赖倒置原则分析

依赖一定会存在于类与类、模块与模块之间。当两个模块之间存在紧密的耦合关系是,最好的方法就是分离接口和实现:在依赖之间定义一个抽象的接口使得高层模块调用接口,而底层模块实现接口的定义,以此来有效控制耦合关系,达到依赖于抽象的设计目标。
抽象的稳定性决定了系统的稳定性,因为抽象是不变的,依赖于抽象是面向对象精髓,也是依赖倒置原则的核心。
依赖于抽象是一个通用的原则,

依赖于抽象是一个通用的原则,而某些时候依赖于细节则是在所难免的,必须权衡在抽象和具体之间的取舍,方法不是一层不变的。依赖于抽象,就是对接口编程,不要对实现编程。

开发封闭原则是面向对象程序设计的原则,而依赖倒置原则类似于实现开放封闭原则的方法。

依赖倒置原则示例

示例一、一个拿c照的司机能够开奔驰小车

//定义奔驰小车类
public class Benz {
	public void run(){
		System.out.println("大奔来了,前面的让一让……");
	}
}
//定义一个司机类(能开奔驰小车)
public class Driverc {
	public void drive(Benz benz){
		System.out.println("老司机开车了,上车的赶快……");
        benz.run();
	}
}
public class Client {
	public static void main( String[] args){
		Benz benz = new Benz( );
		Driverc driver = new DriverC();
        //让司机去开奔驰车
		driver.drive(benz) ;
	}
}

程序分析:
上面的程序实现了拿C照的司机能够开奔驰小车。那么拿C照的司机就只能开奔驰小车吗?
拿C照的司机还能够开BMW,Auto…
但是在上面的程序中,司机类已经依赖了Benz类**(依赖与具体的Benz类,违反了依赖倒置原则)。那么如果要开BMW,就需要修改上面的程序。(违反了开放封闭原则)**

程序优化:可以将车抽象出来,让司机依赖于一个抽象的车

//定义小车接口
public interface Car {
	public void run();
}
//定义奔驰小车类(实现car接口)
public class Benz implements Car{
	@override
	public void run(){
		System.out.print1n("大奔来了,前面的让一让……");
	}
}

//BMW类,实现car接口
public class BMw implements Car i
	@override
	public void run() {
		System.out.println("没见吗?别摸我……");
	}
}
//定义一个司机类(能开小车)
public class Driverc {
	public void drive(car car){
		System.out.println("老司机开车了,上车的赶快……");
        car.run();
}
public class client {
	public static void main(String[] args) i
		Car car1 = new Benz();
		Car car2 = new BMIW();
		Driverc driver = new DriverC();
   		 //让司机去开奔驰车
		driver.drive(car1);
    	//让司机去开BMW
    	driver.drive(car2);
	}
}

程序分析:
利用依赖倒置原则将程序进行优化后,能到达到拿c照的司机可以开任何的小车。程序的可扩展性就大大提高了。并且封闭了换车的变更。(利用了依赖倒置原则之后,也是实现了开放封闭原则)

里氏替换原则

定义

里氏替换原则的核心思想是:子类必须能够替换其基类。这一思想体现为对继承机制的约束规范,只有子类能够替换基类时,才能保证系统在运行期内识别子类,这是保证继承复用的基础。在父类和子类的具体行为中,必须严格把握继承层次中的关系和特征,将基类替换为子类,程序的行为不会发生任何变化。同时,这一约束反过来则是不成立的,子类可以替换基类,但是基类不一定能替换子类(因为子类继承父类之后,可能会扩充一些自己所特有的属性和方法——这是父类将无法完全替换子类)。

基于该原则可知:在继承时,子类尽量不要重写父类的方法。

经典问题

1、鸵鸟飞鸟:

比如“鸟”是基类,这个基类有一个“飞翔”的行为。当“鸵鸟”继承了“鸟”,这就会引起麻烦,覆写基类“飞翔”的行为吧,这样就不再符合里氏替换原则。“鸵鸟”是不能替换它的基类了。

2、正方形不是长方形

“长方形”是基类,“正方形”是一种特殊的长方形,理所应当“正方形”是“长方形”的子类。“长方形”有单独改变长或宽的行为,对于“正方形”来说,就得改写这两个行为以保证长等于宽。这样就违背了里氏替换原则。当长方形调整了长,又调整宽,在算面积的时候。正方形这个子类就会出错。

分析

Liskov替换原则,主要着眼于多态建立在继承的基础上,因此只有遵循了Liskov替换原则,才能保证继承复用是可靠的。
实现的方法是面向接口编程:将公共部分抽象为基类接口或抽象类,通过继承父类,在子类中通过覆写父类的方法实现新的方式支持同样的职责。

Liskov替换原则是关于继承机制的设计原则,违反了Liskov替换原则就必然导致违反开放封闭原则。
Liskov替换原则能够保证系统具有良好的拓展性,同时实现基于多态的抽象机制,能够减少代码冗余,避免运行期的类型判别。

代码:

package com.zsn.test;

public class A {
    public void fun(int a, int b) {
        System.out.println(a + "+" + b + "=" + (a + b));
    }
}

class B extends A {
    @Override
    public void fun(int a, int b) {
        System.out.println(a + "-" + b + "=" + (a - b));
    }
}

class demo {
    public static void main(String[] args) {
        System.out.println("父类的运行结果");
        A a = new A();
        a.fun(1, 2);
        //父类存在的地方,可以用子类替代
        //子类B替代父类A
        System.out.println("子类替代父类后的运行结果");
        B b = new B();
        b.fun(1, 2);
    }
}

接口隔离原则

定义

接口隔离原则的核心思想是:使用多个小的专门的接口,而不要使用一个大的总接口。

分析

​ 接口隔离原则体现在:接口应该是内聚的,应该避免“胖”接口。一个类对另外一个类的依赖应该建立在最小的接口上,不要强迫依赖不用的方法,这是一种接口污染
​ 接口隔离强调接口的单一性。而胖接口存在明显的弊端,会导致实现的类型必须完全实现接口的所有方法等(从语法上来说:一个类实现一个接口那么就必须要实现接口中的所有方法);而某些时候,实现类型并非需要所有的接口定义,在设计上这是“浪费”,而且在实施上这会带来潜在的问题,对胖接口的修改将导致一连串的客户端程序需要修改,有时候这是一种灾难。在这种情况下,将胖接口分解为多个特点的定制化方法,使得客户端仅仅依赖于它们的实际调用的方法,从而解除了客户端不会依赖于它们不用的方法(将胖接口进行拆分)。

分离的手段主要有以下两种:

1、委托分离,通过增加一个新的类型来委托客户的请求,隔离客户和接口的直接依赖,但是会增加系统的开销。

2、多重继承分离,通过接口多继承来实现客户的需求,这种方式是较好的。

代码:

/**
* 定义接口1,里面定义了N个方法
*/
public interface Interface1{
    void fun1();
	void fun2();
    void fun3();
    void fun4();
    void fun5();
}

现在有一个实现类 需要fun1,fun2两个方法,那么当这个实现类实现interface1的时候却实现了5个方法

/**
* Class1类中具有多个无用的方法。
* 虽然在这里可以不用去写太多的代码,但是在调用的时候会引起混乱
*/
public class Class1 implements Interface1{
	@Override
	public void fun1() {
		system.out.println("这个方法是有用的.....");
	}
	@Override
	public void fun2() {
		System.out.println("这个方法是有用的.....");
	}
	@Override
	public void fun3() {
        
    }
    @Override
	public void fun4() {
        
    }
    @Override
	public void fun5() {
        
    }
}

利用接口隔离原则优化代码:

/**
* 首先将胖接口根据具体的需求隔离成多个小的接口
*/
public interface Interface1{
    void fun1();
	void fun2();
}
public interface Interface2{
    void fun3();
	void fun4();
}
public interface Interface3{  
	void fun5();
}
//如果一个类需要实现方法fun1,fun2,fun5。再定义一个接口,继承接口1和接口3
//这时,接口4利用接口的多继承特性,就具有了三个方法的定义
public interface Interface4 extends Interface1, Interface3{  

}
//实现类实现接口4
public class Class1 implements Interface4{
	@Override
	public void fun1() {
		
	}
	@Override
	public void fun2() {
		
	}
    @Override
	public void fun5() {
        
    }
}

接口隔离注意事项

接口尽量小,但是要有限度。对接口进行细化可以提高程序设计灵活性是不争的事实,但是如果过小,则会造成接口数量过多,使设计复杂化。所以一定要适度。
为依赖接口的类定制服务,只暴露给调用的类它需要的方法,它不需要的方法则隐藏起来。只有专注地为一个模块提供定制服务,才能建立最小的依赖关系。提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情

合成复用原则

合成复用原则定义:

合成复用原则又被称为组合/聚合复用原则,其定义如下:

尽量使用对象组合,而不是继承来达到复用的目的。也就是,不要为了继承而继承。

复用的示例:

在此,以数据访问层获取数据库连接,同时操作用户数据为例。
创建一个数据库连接类,内部提供一个获取数据库连接的方法

public class DBConnection {
	public String getConnection() {
	return "MySQL数据库连接";
	}
}

创建一个用户数据访问类,基础数据库连接类的同时,提供一个新增用户的方法。

public class UserDao extends DBConnection {
	public void addUser(){
	String connection = super.getConnection();//省略后续添加客户的操作
	}
}

测试:

public class Test {
	public static void main(String[] args){
		UserDao userDao = new UserDao();
     	userDao.addUser();
	}
}

输出:

使用MySQL数据库连接添加用户

新的需求
假设现在新旧系统合并,需要将旧有系统的Oracle 数据库中的数据接入到新系统中。
但是,目前DBConnection 中只会返回MySQL数据库的连接。
其实也简单,我们在DBConnection中再新增一个方法用于获取 Oracle 数据库的连接。功能实现上其实是没什么问题的,但是这样会违反开闭原则。
那如何利用合成复用原则对示例代码进行重构?

这里数据库连接类是一个抽象类,内部包含一个获取数据库连接的抽象方法,具体获取哪种数据库的连接则交于具体的子类去实现。

public abstract class DBConnection {
	public abstract String getConnection();
}

创建具体的数据库连接子类

public class MySQLConnection extends DBConnection {
	@Override
	public String getConnection() {
		return "MySQL数据库连接";
}
public class OracleConnection extends DBConnection {
	@Override
	public String getConnection() {
		return "Oracle数据库连接";
}

通过组合的方法将数据库连接注入到用户数据访问层当中。

public class UserDao {
	private DBConnection connection;
    
	public void setConnection(DBConnection connection) {
		this.connection = connection;
	}
	public void addUser() {
	//省略后续利用connection添加客户的操作
	}
}

测试:

public class Test {
public static void main(String[] args) {
	UserDao userDao = new UserDao();
	userDao.setConnection(new MySQLConnection());
    userDao.addUser();
    
	userDao.setConnection(new OracleConnection());
    userDao.addUser();
	}
}

输出:

使用MySQL数据库连接添加用户

使用Oracle数据库连接添加用户

小结:

  • 使用继承的方式来进行代码复用,缺点在于继承来的方法相当于是自身的方法,那么这个方法如果在需求发生变更的时候,就无法改变(必须要修改源代码来达到目的)。这破坏开放封闭原则。
  • 而使用合成(组合)复用,方法是其他类中的方法,在本类中只是去调用其他的类。所以可以使用依赖倒置原则,可以达到在需求发生变更的时候,扩展现有代码达到目的。

合成复用原则分析

1、合成复用原则就是指在一个新的对象里通过关联关系(包括组合关系和聚合关系)来使用一些已有的对象,使之成为新对象的一部分;新对象通过委派调用已有对象的方法达到复用其已有功能的目的。简言之:要尽量使用组合/聚合关系,少用继承。
2 、在面向对象设计中,可以通过两种基本方法在不同的环境中复用已有的设计和实现,即通过组合/聚合关系或通过继承。

- 继承复用:实现简单,易于扩展。破坏系统的封装性,从基类继承而来的实现是静态的,不可能在运行时发生改变,没有足够的灵活性;只能在有限的环境中使用。(“白箱”复用)
  • 组合/聚合复用:耦合度相对较低,选择性地调用成员对象的操作;可以在运行时动态进行。(“黑箱”复用)

3、组合/聚合可以使系统更加灵活,类与类之间的耦合度降低,一个类的变化对其他类造成的影响相对较少,因此一般首选使用组合/聚 合来实现复用;

其次才考虑继承,在使用继承时,需要严格遵循里氏代换原则,有效使用继承会有助于对问题的理解,降低复杂度,而滥用继承反而会增加系统构建和维护的难度以及系统的复杂度,因此需要慎重使用继承复用。

迪米特法则

迪米特法则定义

迪米特法则(Law of Demeter, LoD)又称为最少知识原则(Least Knowledge Principle,LKP),

它有多种定义方法,其中几种典型定义如下:

(1)不要和“陌生人”说话。
(2)只与你的直接朋友通信。
(3)每一个软件单位对其他的单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位。

迪米特法则示例:

示例场景:老板让超市员工查看商品的总数量

商品类

public class Goods{
    
}

员工类

public class Staff {
	public void checkNumber(List<Goods> goods) {
		System.out.println("目前超市内商品总数为: "+goods.size());
	}
}

老板类

public class Boss {
	public void requireCheckNumber(Staff staff) {
		List<Goods> goodsList = new ArrayList<Goods>();
        for (int i = 0; i<50; i++) {
			goodsList.add(new Goods());
		}
		staff.checkNumber(goodsList);	
	}
}

测试类

public class Test{
    public static void main(String[] args){
        Boss boss = new Boss();
        Staff staff = new Staff();
		boss.requireCheckNumber(staff);
	}
}

问题分析:
老板指挥员工做事情(清点商品)。但是,在老板内中居然出现了商品类Goods。现在如下修改。

老板类

public class Boss {
	public void requireCheckNumber(Staff staff) {
		staff.checkNumber();
	}
}

员工类

public class Staff {
	public void checkNumber() {
		List<Goods> goodsList = new ArrayList<Goods>();
        for ( int i = 0; i < 50; i++) {
			goodsList.add(new Goods());
		}
		System.out.println("目前超市内商品总数为: "+goodsList.size());
	}
}

经过如上的改造,老板类不再与商品类发生直接关系。

迪米特法则分析

​ 1、迪米特法则就是指一个软件实体应当尽可能少的与其他实体发生相互作用(与其他实体发生关系越多,那么耦合度就越高)。这样,当一个模块修改时,就会尽量少的影响其他的模块,扩展会相对容易,这是对软件实体之间通信的限制,它要求限制软件实体之间通信的宽度和深度。
​ 2、在迪米特法则中,对于一个对象,其朋友包括以下几类:
​ (1)当前对象本身(this);

​ (2) 以参数形式传入到当前对象方法中的对象;

​ (3)当前对象的成员对象;

​ (4) 如果当前对象的成员对象是一个集合,那么集合中的元素也都是朋友;

​ (5)当前对象所创建的对象。任何一个对象,

如果满足上面的条件之一,就是当前对象的“朋友”,否则就是“陌生

​ 3、迪米特法则可分为狭义法则和广义法则。在狭义的迪米特法则中,如果两个类之间不必彼此直接通信,那么这两个类就不应当发生直接的相互作用,如果其中的一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。
​ 4、狭义的迪米特法则:可以降低类之间的耦合,但是会在系统中增加大量的小方法并散落在系统的各个角落,它可以使一个系统的局部设计简化,因为每一个局部都不会和远距离的对象有直接的关联,但是也会造成系统的不同模块之间的通信效率降低,使得系统的不同模块之间不容易协调。
​ 5、广义的迪米特法则:指对对象之间的信息流量、流向以及信息的影响的控制,主要是对信息隐藏的控制。信息的隐藏可以使各个子系统之间脱耦,从而允许它们独立地优化、使用和修改,同时可以促进软件的复用,由于每一个模块都不依赖于其他模块而存在,因此每一个模块都可以独立地在其他的地方使用。一个系统的规模越大,信息的隐藏就越重要,而信息隐藏的重要性也就越明显。

如何使用迪米特法则

迪米特法则的主要用途在于控制信息的过载:
1、在类的划分上,应当尽量创建松耦合的类,类之间的耦合度越低,就越有利于复用,一个处在松耦合中的类一旦被修改,不会对关联的类造成太大波及;

2、在类的结构设计上,每一个类都应当尽量降低其成员变量和成员函数的访问权限;

3、在类的设计上,只要有可能,一个类型应当设计成不变类;

4、在对其他类的引用上,一个对象对其他对象的引用应当降到最低。

设计原则总结

单一职责原则:要求在软件系统中,一个类只负责一个功能领域中的相应职责。
开闭原则要求:一个软件实体应当对扩展开放,对修改关闭,即在不修改源代码的基础上扩展一个系统的行为。
里氏代换原则:可以通俗表述为在软件中如果能够使用基类对象,那么一定能够使用其子类对象。
依赖倒转原则:要求抽象不应该依赖于细节,细节应该依赖于抽象;要针对接口编程,不要针对实现编程。
接口隔离原则:要求客户端不应该依赖那些它不需要的接口,即将一些大的接口细化成一些小的接口供客户端使用。
合成复用原则:要求复用时尽量使用对象组合,而不使用继承。
方法的话,可以通过第三者转发这个调用。
​ 4、狭义的迪米特法则:可以降低类之间的耦合,但是会在系统中增加大量的小方法并散落在系统的各个角落,它可以使一个系统的局部设计简化,因为每一个局部都不会和远距离的对象有直接的关联,但是也会造成系统的不同模块之间的通信效率降低,使得系统的不同模块之间不容易协调。
​ 5、广义的迪米特法则:指对对象之间的信息流量、流向以及信息的影响的控制,主要是对信息隐藏的控制。信息的隐藏可以使各个子系统之间脱耦,从而允许它们独立地优化、使用和修改,同时可以促进软件的复用,由于每一个模块都不依赖于其他模块而存在,因此每一个模块都可以独立地在其他的地方使用。一个系统的规模越大,信息的隐藏就越重要,而信息隐藏的重要性也就越明显。

如何使用迪米特法则

迪米特法则的主要用途在于控制信息的过载:
1、在类的划分上,应当尽量创建松耦合的类,类之间的耦合度越低,就越有利于复用,一个处在松耦合中的类一旦被修改,不会对关联的类造成太大波及;

2、在类的结构设计上,每一个类都应当尽量降低其成员变量和成员函数的访问权限;

3、在类的设计上,只要有可能,一个类型应当设计成不变类;

4、在对其他类的引用上,一个对象对其他对象的引用应当降到最低。

设计原则总结

单一职责原则:要求在软件系统中,一个类只负责一个功能领域中的相应职责。
开闭原则要求:一个软件实体应当对扩展开放,对修改关闭,即在不修改源代码的基础上扩展一个系统的行为。
里氏代换原则:可以通俗表述为在软件中如果能够使用基类对象,那么一定能够使用其子类对象。
依赖倒转原则:要求抽象不应该依赖于细节,细节应该依赖于抽象;要针对接口编程,不要针对实现编程。
接口隔离原则:要求客户端不应该依赖那些它不需要的接口,即将一些大的接口细化成一些小的接口供客户端使用。
合成复用原则:要求复用时尽量使用对象组合,而不使用继承。
迪米特法则:要求一个软件实体应当尽可能少的与其他实体发生相互作用。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值