一、设计模式七大原则
设计模式的目的
- 代码重用性 (即:相同功能的代码,不用多次编写)
- 可读性 (即:编程规范性, 便于其他程序员的阅读和理解)
- 可扩展性 (即:当需要增加新的功能时,非常的方便,称为可维护)
- 可靠性 (即:当我们增加新的功能后,对原来的功能没有影响)
- 高内聚低耦合:使程序呈现高内聚,低耦合的特性
有点官方,反正就是牛逼!
七大原则:
1.单一职责原则
每个类只负责一个职责
2.接口隔离原则
将接口细分为多个接口,保证实现接口的类不重写自己不需要的方法
3.依赖倒转原则
用 抽象类 或者 接口,然后由具体的类去继承、实现它,重写其方法,实现依赖倒转 类似 多态
注意:变量的声明类型尽量是抽象类或接口!!!
4.里氏替换原则
子类中尽量不要重写父类的方法
5.开闭原则
一个软件实体 如类、模块、函数 应该对 扩展新功能 开放,对修改旧功能关闭。
接口或者抽象类 内置 方法,让其实现类 重写方法满足其需要
6.迪米特原则
简单来说: 一个类中 只与 [ 直接的朋友 ] 产生依赖 如果是 陌生类,就违背 迪米特法则
什么是直接朋友:
1、成员变量: A类中的 全局变量 有 private B b; B是A的直接朋友
2、方法参数:A类中 M1方法的 请求参数 为 B b -> m1(B b){} A与B是直接朋友
3、方法返回值的类: A类中的方法 M2 的返回值为B -> public B M2(){} A与B是直接朋友
7.合成复用原则
类和类之间 尽量使用 合成 和聚合的方式 而不是使用 继承
1.1、单一职责原则
介绍:
一个类应该只负责一项职责。如类 A 负责两个不同职责:职责 1,职责 2。当职责 1 需求变更 而改变 A 时,可能造成职责 2 执行错误
以 交通工具为 案例
不使用 单一职责原则 Demo:
public class A_SingleResponsibilityPrinciple {
public static void main(String[] args) {
Vehicle vehicle = new Vehicle();
vehicle.run("汽车");
vehicle.run("飞机");
vehicle.run("鲨鱼");
}
static class Vehicle{
public void run(String vehicle){
System.out.println(vehicle + "在公路上跑");
}
}
}
理解:Vehicle 类的 run 方法 职责是 在 公路上跑 的职责,并不负责在 天上飞、水里游的职责。
使用 单一职责原则 Demo:
public class C_SingleResponsibilityPrinciple {
public static void main(String[] args) {
Vehicle vehicle = new Vehicle();
vehicle.run("汽车");
vehicle.runAir("飞机");
vehicle.runWater("鲨鱼");
}
static class Vehicle{
public void run(String vehicle){
System.out.println(vehicle + "在公路上跑");
}
public void runAir(String vehicle){
System.out.println(vehicle + "在天上跑");
}
public void runWater(String vehicle){
System.out.println(vehicle + "在水里跑");
}
}
}
理解:Vehicle 类的
run方法 只负责在 公路上跑 ;
runAir 负责在天上跑
runWater负责在水里跑
每个方法都只对应 一个职责,是为 单一职责原则。
单一职责原则注意事项和细节
- 降低类的复杂度,一个类只负责一项职责。
- 提高类的可读性,可维护性
- 降低变更引起的风险
- 通常情况下,我们应当遵守单一职责原则,只有逻辑足够简单,才可以
在代码级违反单一职责原则;只有类中 方法数量足够少,可以在方法级别保持单一职责原则
1.2、接口隔离原则
接口隔离原则:一个类不需要实现他不需要的接口。即:一个类对另一个类的依赖应该建立在最小的接口上。
理解:
我们一个类A 通过 接口去依赖另一个类B 我们希望这个接口是最小的(不包含A不需要的方法)
就是 原本接口 I1有很多方法,A只想用其中 几个 就把其中几个单独拿出来做个接口I2 ,B去继承这来接口
不使用接口隔离原则的Demo
/**
* 接口隔离原则:一个类不需要实现他不需要的接口。即:一个类对另一个类的依赖应该建立在最小的接口上
* 翻译一下就是:我们一个类A 通过 接口去依赖另一个类B 我门希望这个接口是最小的(不包含A不需要的方法)
* 就是 原本接口I1 有很多方法,A只想用其中 几个 就把其中几个单独拿出来做个接口I22 ,B去继承这来接口
*
* 不使用 接口隔离原则
*/
public class A_InterfaceLsolationPrinciple {
public static void main(String[] args) {
// A 通过 接口 去依赖B类 A只用 1 2 3 但 B 实现了接口的所有方法【12345】 不符合 接口的 隔离原则
A a = new A();
a.do1(new B());
a.do2(new B());
a.do3(new B());
C c = new C();
c.do1(new D());
c.do4(new D());
c.do5(new D());
}
// 接口
interface Interface1 {
void run1();
void run2();
void run3();
void run4();
void run5();
}
// 接口
interface Interface3 extends Interface1{
void run6();
}
static class B implements Interface1 {
@Override
public void run1() {
System.out.println("B实现 run1");
}
@Override
public void run2() {
System.out.println("B实现 run2");
}
@Override
public void run3() {
System.out.println("B实现 run3");
}
@Override
public void run4() {
System.out.println("B实现 run4");
}
@Override
public void run5() {
System.out.println("B实现 run5");
}
}
static class D implements Interface1{
@Override
public void run1() {
System.out.println("D实现 run1");
}
@Override
public void run2() {
System.out.println("D实现 run2");
}
@Override
public void run3() {
System.out.println("D实现 run3");
}
@Override
public void run4() {
System.out.println("D实现 run4");
}
@Override
public void run5() {
System.out.println("D实现 run5");
}
}
/**
* A 通过接口 Interface1 依赖(使用)B类 ,但 只用 1 2 3 方法
*/
static class A{
public void do1(Interface1 i){
i.run1();
}
public void do2(Interface1 i){
i.run2();
}
public void do3(Interface1 i){
i.run3();
}
}
/**
* C 通过接口 Interface1 依赖(使用)D类 ,但 只用 1 4 5 方法
*/
static class C{
public void do1(Interface1 i){
i.run1();
}
public void do4(Interface1 i){
i.run4();
}
public void do5(Interface1 i){
i.run5();
}
}
}
代码解释:
一个接口 Interface1 有5个方法
// 接口
interface Interface1 {
void run1();
void run2();
void run3();
void run4();
void run5();
}
B、D两个类实现 接口 Interface1 .并重写了 接口的五个方法
static class B implements Interface1 {
@Override
public void run1() {
System.out.println("B实现 run1");
}
@Override
public void run2() {
System.out.println("B实现 run2");
}
@Override
public void run3() {
System.out.println("B实现 run3");
}
@Override
public void run4() {
System.out.println("B实现 run4");
}
@Override
public void run5() {
System.out.println("B实现 run5");
}
}
static class D implements Interface1{
@Override
public void run1() {
System.out.println("D实现 run1");
}
@Override
public void run2() {
System.out.println("D实现 run2");
}
@Override
public void run3() {
System.out.println("D实现 run3");
}
@Override
public void run4() {
System.out.println("D实现 run4");
}
@Override
public void run5() {
System.out.println("D实现 run5");
}
}
重点来了:
A类 通过接口 Interface1 依赖(使用)B类,但 只用 1 2 3 方法
A 通过 接口 去依赖B类 A只用 1 2 3 但 B 实现了接口的所有方法【12345】 不符合 接口的 隔离原则
C 通过接口 Interface1 依赖(使用)D类 ,但 只用 1 4 5 方法
C 通过 接口 去依赖D类 C只用 1 4 5 但 D 实现了接口的所有方法【12345】 不符合 接口的 隔离原则
static class A{
public void do1(Interface1 i){
i.run1();
}
public void do2(Interface1 i){
i.run2();
}
public void do3(Interface1 i){
i.run3();
}
}
/**
* C 通过接口 Interface1 依赖(使用)D类 ,但 只用 1 4 5 方法
*/
static class C{
public void do1(Interface1 i){
i.run1();
}
public void do4(Interface1 i){
i.run4();
}
public void do5(Interface1 i){
i.run5();
}
}
main 方法
public static void main(String[] args) {
// A 通过 接口 去依赖B类 A只用 1 2 3 但 B 实现了接口的所有方法【12345】 不符合 接口的 隔离原则
A a = new A();
a.do1(new B());
a.do2(new B());
a.do3(new B());
C c = new C();
c.do1(new D());
c.do4(new D());
c.do5(new D());
}
改进 使用接口隔离原则
public class B_InterfaceLsolationPrinciple {
public static void main(String[] args) {
A a = new A();
a.do1(new B());
a.do2(new B());
a.do3(new B());
C c = new C();
c.do1(new D());
c.do4(new D());
c.do5(new D());
}
// 接口
interface Interface1 {
void run1();
}
// 接口
interface Interface2 {
void run2();
void run3();
}
// 接口
interface Interface3 {
void run4();
void run5();
}
static class B implements Interface1 ,Interface2{
Interface1 i1 =new B();
@Override
public void run1() {
System.out.println("B实现 run1");
}
@Override
public void run2() {
System.out.println("B实现 run2");
}
@Override
public void run3() {
System.out.println("B实现 run3");
}
}
static class D implements Interface1,Interface3{
@Override
public void run1() {
System.out.println("D实现 run1");
}
@Override
public void run4() {
System.out.println("D实现 run4");
}
@Override
public void run5() {
System.out.println("D实现 run5");
}
}
/**
* A 通过接口 Interface1 依赖(使用)B类 ,但 只用 1 2 3 方法
*/
static class A{
public void do1(Interface1 i){
i.run1();
}
public void do2(Interface2 i){
i.run2();
}
public void do3(Interface2 i){
i.run3();
}
}
/**
* C 通过接口 Interface1 依赖(使用)D类 ,但 只用 1 4 5 方法
*/
static class C{
public void do1(Interface1 i){
i.run1();
}
public void do4(Interface3 i){
i.run4();
}
public void do5(Interface3 i){
i.run5();
}
}
}
核心代码解释
将 原本的 接口一拆分成三个接口,分别为123
因为A 类只用 1 2 3 方法 ;D 只用 1 4 5 方法
// 接口
interface Interface1 {
void run1();
}
// 接口
interface Interface2 {
void run2();
void run3();
}
// 接口
interface Interface3 {
void run4();
void run5();
}
B 类实现 接口 1、2 并重写其方法 A 类只用 1 2 3 方法
D 类实现 接口 1、3并重写其方法 D 只用 1 4 5 方法
static class B implements Interface1 ,Interface2{
Interface1 i1 =new B();
@Override
public void run1() {
System.out.println("B实现 run1");
}
@Override
public void run2() {
System.out.println("B实现 run2");
}
@Override
public void run3() {
System.out.println("B实现 run3");
}
}
static class D implements Interface1,Interface3{
@Override
public void run1() {
System.out.println("D实现 run1");
}
@Override
public void run4() {
System.out.println("D实现 run4");
}
@Override
public void run5() {
System.out.println("D实现 run5");
}
}
通过拆分接口、实现接口 隔离原则
1.3、依赖倒置原则
说点 字都认识 但放在一起就不认识的吧
- 高层模块不应该依赖低层模块,二者都应该依赖其抽象
- 抽象不应该依赖细节,细节应该依赖抽象
- 依赖倒转(倒置)的中心思想是面向接口编程
- 依赖倒转原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架 构比以细节为基础的架构要稳定的多。在 java 中,抽象指的是接口或抽象类,细节就是具体的实现类
- 使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完 成
应该 、大概、也许就是 用 抽象类 或者 接口,然后由具体的类去继承、实现它,重写其方法,实现依赖倒转 类似 多态
注意:变量的声明类型尽量是抽象类或接口!!!
Person 接收消息的功能
不用 依赖倒转原则
public class A_RelyOnTheInversionPrinciple {
public static void main(String[] args) {
new Person().receive(new Email());
}
static class Email{
public String getInfo(){
return "电子邮件信息:hello";
}
}
/**
* 完成Person接收消息的功能
*/
static class Person{
public void receive(Email email){
System.out.println(email.getInfo());
}
}
}
理解:
1、简单比较容易想到
2、如果我们获取的对象是 微信、短信等,而不是 电子邮件,那么就要增加 新类 person 也要增加对应的接收方法
改进:
引入一个 抽象接口:IReceive,表示接收者, 这样Person类 与IReceive发生依赖
Email \微信 等属于接收的范畴,可以实现 IReceive接口
这就是 依赖倒转原则 类似 多态
public class B_RelyOnTheInversionPrinciple {
public static void main(String[] args) {
new Person().receive(new Email());
new Person().receive(new QQ());
}
// 收到消息的 接口
interface IReceive{
String getInfo();
}
// 邮件收到消息
static class Email implements IReceive{
@Override
public String getInfo() {
return "电子邮件信息:hello";
}
}
// QQ收到消息
static class QQ implements IReceive{
@Override
public String getInfo() {
return "QQ信息:hello";
}
}
/**
* 完成Person接收消息的功能
* 依赖倒转原则 实现: 引入一个 抽象接口:IReceive,表示接收者, 这样Person类 与IReceive发生依赖
* Email 、微信 等属于接收的范畴,可以实现 IReceive接口
*
* 类似 多态
*
*/
static class Person{
public void receive(IReceive receive){
System.out.println(receive.getInfo());
}
}
}
代码解释:
上面说到:抽象不应该依赖细节,细节应该依赖抽象
所以 先定义个抽象(接口),然后让细节(接口的实现类)去展示具体细节
1、首先定义 一个获取消息的接口
// 收到消息的 接口
interface IReceive{
String getInfo();
}
2、然后让 qq 、微信…去实现接口
// 邮件收到消息
static class Email implements IReceive{
@Override
public String getInfo() {
return "电子邮件信息:hello";
}
}
// QQ收到消息
static class QQ implements IReceive{
@Override
public String getInfo() {
return "QQ信息:hello";
}
}
实现了 细节依赖抽象啦。
人获取消息的方法:
static class Person{
public void receive(IReceive receive){
System.out.println(receive.getInfo());
}
}
main:
public static void main(String[] args) {
new Person().receive(new Email());
new Person().receive(new QQ());
}
1.3.1、依赖传递的方式
多说几句。。。。。。
依赖关系的传递三种方式
1、接口传递
2、构造方法传递
3、setter方法传递
1、接口传递
interface IOpenAndClose {
void open(ITV tv);
}
interface ITV {
void play();
}
static class hand implements ITV {
@Override
public void play() {
System.out.println("用手关");
}
}
// 实现接口
static class OpenAndClose implements IOpenAndClose {
@Override
public void open(ITV tv) {
tv.play();
}
}
解释:
OpenAndClose 方法 实现了 IOpenAndClose 接口 ,但是呢,IOpenAndClose 的方法使用了
ITV接口 所以 OpenAndClose 就 通过 IOpenAndClose (依赖)使用了 ITV 是为 : 接口传递
2、构造方法传递
public class D_DependOnTheTransfer {
public static void main(String[] args) {
new OpenAndClose(new hand()).open();
}
/**
* 方式 2、构造方法传递
* <p>
* 开关接口
*/
interface IOpenAndClose {
void open();
}
interface ITV {
void play();
}
static class hand implements ITV {
@Override
public void play() {
System.out.println("用手关");
}
}
// 实现接口
static class OpenAndClose implements IOpenAndClose {
public ITV tv;
public OpenAndClose(ITV tv){ // 构造器
this.tv = tv;
}
@Override
public void open() {
this.tv.play();
}
}
}
解释:
static class OpenAndClose implements IOpenAndClose {
public ITV tv;
public OpenAndClose(ITV tv){ // 构造器
this.tv = tv;
}
@Override
public void open() {
this.tv.play();
}
}
OpenAndClose 类的构造方法 参数为 ITV接口,so 通过 构造方法和 ITV产生了依赖
3、setter 方法传递
public class E_DependOnTheTransfer {
public static void main(String[] args) {
OpenAndClose openAndClose = new OpenAndClose();
openAndClose.setTv(new hand());
openAndClose.open();
}
/**
* 方式 3、setter方法传递
* <p>
* 开关接口
*/
interface IOpenAndClose {
void open();
void setTv(ITV tv);
}
interface ITV {
void play();
}
static class hand implements ITV {
@Override
public void play() {
System.out.println("用手关");
}
}
// 实现接口
static class OpenAndClose implements IOpenAndClose {
public ITV tv;
@Override
public void open() {
this.tv.play();
}
@Override
public void setTv(ITV tv) {
this.tv = tv;
}
}
}
解释:
static class OpenAndClose implements IOpenAndClose {
public ITV tv;
@Override
public void open() {
this.tv.play();
}
@Override
public void setTv(ITV tv) {
this.tv = tv;
}
}
此处 通过 set方法的参数 ITV ,OpenAndClose 和 ITV 产生了依赖
依赖倒转原则的注意事项和细节:
- 低层模块尽量都要有抽象类或接口,或者两者都有,程序稳定性更好.
- 变量的声明类型尽量是抽象类或接口, 这样我们的变量引用和实际对象间,就存在一个缓冲层,利于程序扩展 和优化
- 继承时遵循里氏替换原则
1.4、里氏替换原则
里氏替换原则的必要性:
继承包含这样一层含义:
1、父类中凡是已经实现好的方法,虽然他不强制要求所有的子类必须遵循这些契约,但是如果子类对这些已经实现的方法任意修改,就会对整个继承体系造成破坏。
2、如果一个类被其他类所继承,则当这个类需要任意修改,就必须考虑到所有的子类,并且父类修改后,所有涉及到子类的功能都可能产生故障
废话不说,就一句话:
子类中尽量不要重写父类的方法
里氏替换原则告诉我们,继承实际上让两个类耦合性增强了,在适当的情况下,可以通过聚合,组合,依赖 来 解决问题。.
案例:
不使用里氏替换原则
static class A {
public int func1(int num1, int num2) {
return num1 - num2;
}
}
static class B extends A {
// 重写了A 的方法
public int func1(int a, int b) {
return a + b;
}
public int func2(int a,int b){
return func1(a,b) + 9;
}
}
public static void main(String[] args) {
A a = new A();
System.out.println("11-3=" + a.func1(11,3));
System.out.println("1-8=" + a.func1(1,8));
System.out.println("====================");
B b = new B();
System.out.println("11-3=" + b.func1(11,3));
System.out.println("1-8=" + b.func1(1,8));
System.out.println("11+3+9=" + b.func2(11,3));
}
解释:B无意中重写的父类的func1方法, 原本func1 是 num1 - num2,重写后 变成 相加, 这样 如果是无意识的话,程序员本来要做 func1的相减功能,但是结果却是相加。造成BUG
改进:
public class B_RichterSubstitutionPrinciple {
/**
* 创建一个更加 基础的基类
*
*/
static class Base{
//更加基础的方法和成员写到Base
}
static class A extends Base{
public int func1(int num1, int num2) {
return num1 - num2;
}
}
static class B extends Base {
// 如果B需要使用A类的方法,使用组合的关系
private A a = new A();
// 仍然想用A 的方法
public int func3(int a,int b){
return this.a.func1(a,b);
}
// 重写了A 的方法
public int func1(int a, int b) {
return a + b;
}
public int func2(int a,int b){
return func1(a,b) + 9;
}
}
public static void main(String[] args) {
A a = new A();
System.out.println("11-3=" + a.func1(11,3));
System.out.println("1-8=" + a.func1(1,8));
System.out.println("====================");
// 因为B类不再继承A 类,因此调用者不会在func1是求减法
B b = new B();
System.out.println("11-3=" + b.func3(11,3));
System.out.println("1-8=" + b.func3(1,8));
System.out.println("1+8=" + b.func1(1,8));
System.out.println("11+3+9=" + b.func2(11,3));
}
}
代码解释:
创建一个更为基础的 类
static class Base{
//更加基础的方法和成员写到Base
}
A、B 类 继承的是 base B类不再继承 A 类 就不存在重写的情况
B类中使用A类的 func1方法是 通过 new A 的方法(组合依赖解决问题)
static class B extends Base {
// 如果B需要使用A类的方法,使用组合的关系
private A a = new A();
// 仍然想用A 的方法
public int func3(int a,int b){
return this.a.func1(a,b);
}
// 重写了A 的方法
public int func1(int a, int b) {
return a + b;
}
public int func2(int a,int b){
return func1(a,b) + 9;
}
}
1.5、开闭原则
最重要 最基础的原则 对扩展开放【提供者】 对修改关闭【使用方】
一个软件实体 如类、模块、函数 应该对 扩展新功能 开放,对修改旧功能关闭。
用抽象 构建 框架、用实体 扩展细节
案例 绘图
不使用开闭原则:
public class A_OpenClosePrinciple {
public static void main(String[] args) {
GraphicEditor graphicEditor = new GraphicEditor();
// 矩形
graphicEditor.drawShape(new GraphicEditor.Rectangle());
// 圆形
graphicEditor.drawShape(new GraphicEditor.Circle());
// 三角形
// 1
graphicEditor.drawShape(new GraphicEditor.Triangle());
}
/**
* 这是一个用于绘图的类
*/
static class GraphicEditor{
// 接收Shape对象,然后 根据type,来绘制不同的图形 【使用方】
public void drawShape(Shape s){
if (s.m_type == 1){
drawRectangle(s);
}else if (s.m_type == 2){
drawCircle(s);
}else if (s.m_type == 3){
// 2
drawTriangle(s);
}
}
// 绘制矩形
public void drawRectangle(Shape s){
System.out.println("绘制矩形");
}
// 绘制圆
public void drawCircle(Shape s){
System.out.println("绘制圆");
}
// 绘制三角形
// 3
public void drawTriangle(Shape s){
System.out.println("绘制三角形");
}
// Shape 类
static class Shape{
int m_type;
}
// 矩形
static class Rectangle extends Shape{
Rectangle(){
super.m_type = 1;
}
}
// 圆
static class Circle extends Shape{
Circle(){
super.m_type = 2;
}
}
// 新增三角形
// 4
static class Triangle extends Shape{
Triangle(){
super.m_type = 3;
}
}
}
}
解释:
- 优点: 好理解,简单易操作
- 缺点:违反了设计模式的开闭原则,对扩展开放【提供者】 对修改关闭【使用方】
即:当我们给类增加功能时,尽量不要修改代码或少修改代码
如下:新增三角形 修改 4处
四处:
1、
// 三角形
// 1
graphicEditor.drawShape(new GraphicEditor.Triangle());
2、
else if (s.m_type == 3){
// 2
drawTriangle(s);
}
3、
// 绘制三角形
// 3
public void drawTriangle(Shape s){
System.out.println("绘制三角形");
}
4、
// 新增三角形
// 4
static class Triangle extends Shape{
Triangle(){
super.m_type = 3;
}
}
改进:
使用开闭原则:
将基类 Shape类 做成 抽象类,并提供一个抽象的draw方法,让子类去实现其需要
public class B_OpenClosePrinciple {
public static void main(String[] args) {
GraphicEditor graphicEditor = new GraphicEditor();
// 矩形
graphicEditor.drawShape(new GraphicEditor.Rectangle());
// 圆形
graphicEditor.drawShape(new GraphicEditor.Circle());
// 三角形
graphicEditor.drawShape(new GraphicEditor.Triangle());
graphicEditor.drawShape(new GraphicEditor.pentagon());
}
/**
* 这是一个用于绘图的类
*/
static class GraphicEditor{
// 接收Shape对象,然后 根据type,来绘制不同的图形 【使用方】
public void drawShape(Shape s){
s.draw();
}
// Shape 类
static abstract class Shape{
public abstract void draw();
}
// 矩形
static class Rectangle extends Shape {
@Override
public void draw() {
System.out.println("绘制矩形");
}
}
// 圆
static class Circle extends Shape {
@Override
public void draw() {
System.out.println("绘制圆");
}
}
// 新增三角形
static class Triangle extends Shape {
@Override
public void draw() {
System.out.println("绘制三角形");
}
}
// 新增五角形
static class pentagon extends Shape {
@Override
public void draw() {
System.out.println("绘制五角形");
}
}
}
}
解释:
创建一个 抽象绘图类
// Shape 类
static abstract class Shape{
public abstract void draw();
}
各个图形继承这个歌类 重写其绘图方法
// 矩形
static class Rectangle extends Shape {
@Override
public void draw() {
System.out.println("绘制矩形");
}
}
这样 每次增加一个图形 只需要 加一个类 继承 Shape接口即可
1.6、迪米特原则
看不懂的官方:
一个类 对自己依赖的类知道的越少越好。也就是说:对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供public方法,不对外泄露任何信息
简单来说: 一个类中 只与 [ 直接的朋友 ] 产生依赖 如果是 陌生类,就违背 迪米特法则
什么是直接朋友:
1、成员变量: A类中的 全局变量 有 private B b; B是A的直接朋友
2、方法参数:A类中 M1方法的 请求参数 为 B b -> m1(B b){} A与B是直接朋友
3、方法返回值的类: A类中的方法 M2 的返回值为B -> public B M2(){} A与B是直接朋友
之外就是 陌生的类。
目的:
1、迪米特原则:降低类之间的耦合
2、只要求降低 耦合度,而不是完全没有
案例:
不使用 迪米特原则
public class A_DemeterPrinciple {
public static void main(String[] args) {
//创建了一个 SchoolManager 对象
SchoolManager schoolManager = new SchoolManager();
//输出学院的员工 id 和 学校总部的员工信息
schoolManager.printAllEmployee(new CollegeManager());
}
//学校总部员工类
static class Employee {
private String id;
public void setId(String id) {
this.id = id;
}
public String getId() {
return id;
}
}
//学院的员工类
static class CollegeEmployee {
private String id;
public void setId(String id) {
this.id = id;
}
public String getId() {
return id;
}
}
// 管理学院员工的管理类
static class CollegeManager {
// 返回学院的所有员工
public List<CollegeEmployee> getAllEmployee() {
List<CollegeEmployee> list = new ArrayList<CollegeEmployee>();
for (int i = 0; i < 10; i++) { // 这里我们增加了 10 个员工到 list
CollegeEmployee emp = new CollegeEmployee();
emp.setId("学院员工 id= " + i);
list.add(emp);
}
return list;
}
}
// 学校管理类
// 分析 SchoolManager 类的直接朋友类有哪些 Employee、CollegeManager
// CollegeEmployee 不是 直接朋友 而是一个陌生类,这样违背了 迪米特法则
static class SchoolManager {
// 返回学校总部的员工
public List<Employee> getAllEmployee() {
List<Employee> list = new ArrayList<Employee>();
for (int i = 0; i < 5; i++) { //这里我们增加了 5 个员工到 list
Employee emp = new Employee();
emp.setId("学校总部员工 id= " + i);
list.add(emp);
}
return list;
}
//该方法完成输出学校总部和学院员工信息(id)
void printAllEmployee(CollegeManager sub) {
/**
* 分析问题
* 1. 这里的 CollegeEmployee 不是 SchoolManager 的直接朋友
* 2. CollegeEmployee 是以局部变量方式出现在 SchoolManager
* 3. 违反了 迪米特法则
*/
//获取到学院员工
List<CollegeEmployee> list1 = sub.getAllEmployee();
System.out.println("----------------学院员工--------------");
for (CollegeEmployee e : list1) {
System.out.println(e.getId());
}
//获取到学校总部员工
List<Employee> list2 = this.getAllEmployee();
System.out.println("------------学校总部员工-----------");
for (Employee e : list2) {
System.out.println(e.getId());
}
}
}
}
其中:
void printAllEmployee(CollegeManager sub) {
//获取到学院员工
List<CollegeEmployee> list1 = sub.getAllEmployee();
System.out.println("----------------学院员工--------------");
for (CollegeEmployee e : list1) {
System.out.println(e.getId());
}
}
这里的 CollegeEmployee 不是 SchoolManager 的直接朋友
CollegeEmployee 是以局部变量方式出现在 SchoolManager
违反了 迪米特法则
改进:
public class B_DemeterPrinciple {
public static void main(String[] args) {
//创建了一个 SchoolManager 对象
SchoolManager schoolManager = new SchoolManager();
//输出学院的员工 id 和 学校总部的员工信息
schoolManager.printAllEmployee(new CollegeManager());
}
//学校总部员工类
static class Employee {
private String id;
public void setId(String id) {
this.id = id;
}
public String getId() {
return id;
}
}
//学院的员工类
static class CollegeEmployee {
private String id;
public void setId(String id) {
this.id = id;
}
public String getId() {
return id;
}
}
// 管理学院员工的管理类
static class CollegeManager {
// 返回学院的所有员工
public List<CollegeEmployee> getAllEmployee() {
List<CollegeEmployee> list = new ArrayList<CollegeEmployee>();
for (int i = 0; i < 10; i++) { // 这里我们增加了 10 个员工到 list
CollegeEmployee emp = new CollegeEmployee();
emp.setId("学院员工 id= " + i);
list.add(emp);
}
return list;
}
// 输出学院员工的信息
public void printEmployee(){
//获取到学院员工
List<CollegeEmployee> list1 = getAllEmployee();
System.out.println("----------------学院员工--------------");
for (CollegeEmployee e : list1) {
System.out.println(e.getId());
}
}
}
// 学校管理类
// 分析 SchoolManager 类的直接朋友类有哪些 Employee、CollegeManager
// CollegeEmployee 不是 直接朋友 而是一个陌生类,这样违背了 迪米特法则
static class SchoolManager {
// 返回学校总部的员工
public List<Employee> getAllEmployee() {
List<Employee> list = new ArrayList<Employee>();
for (int i = 0; i < 5; i++) { //这里我们增加了 5 个员工到 list
Employee emp = new Employee();
emp.setId("学校总部员工 id= " + i);
list.add(emp);
}
return list;
}
//该方法完成输出学校总部和学院员工信息(id)
void printAllEmployee(CollegeManager sub) {
/**
* 1. 这里的 CollegeEmployee 不是 SchoolManager 的直接朋友
* 2. CollegeEmployee 是以局部变量方式出现在 SchoolManager
* 3. 违反了 迪米特法则
*
* 改进:
* 1、将输出学院的员工方法,封装到 CollegeManager 对应到 被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部
*
*/
//获取到学院员工
sub.printEmployee();
//获取到学校总部员工
List<Employee> list2 = this.getAllEmployee();
System.out.println("------------学校总部员工-----------");
for (Employee e : list2) {
System.out.println(e.getId());
}
}
}
}
重点:
void printAllEmployee(CollegeManager sub) {
//获取到学院员工
sub.printEmployee();
//获取到学校总部员工
List<Employee> list2 = this.getAllEmployee();
System.out.println("------------学校总部员工-----------");
for (Employee e : list2) {
System.out.println(e.getId());
}
}
将输出学院的员工方法,封装到 CollegeManager 对应到 被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部
// 输出学院员工的信息
public void printEmployee(){
//获取到学院员工
List<CollegeEmployee> list1 = getAllEmployee();
System.out.println("----------------学院员工--------------");
for (CollegeEmployee e : list1) {
System.out.println(e.getId());
}
}