设计模式七大原则
设计模式七大原则
- 单一职责原则
- 对类来说,一个类只负责一项职责。比如说A类有两个职责,职责1,职责2;那么就可以把A类分为两个类,这就是遵循了单一职责原则
- 接口隔离原则
- 客户端类不需要依赖它不需要的接口,既一个类对另一类依赖应该建立在最小接口上
- 简单的说客户类,要使用一个接口的时候,不要把用不到的方法也实现了
- 依赖倒转原则
- 高层模块不要依赖底层模块,二者都要依赖其抽象
- 抽象不要依赖细节,细节应该依赖抽象
- 依赖倒转(倒置)的中心思想就是面向接口编程
- 依赖倒转原则基于这样的设计理念;对于细节的多边形,抽象要稳定的多。以抽象为基础搭建的架构要比以细节为基础的架构稳定的多。在Java中抽象就是接口或抽象类,细节就是实体类
- 使用接口或抽象类目的是指定好的规范,而不涉及任何具体操作。把展示细节部分交给他们的实现类完成
- 里氏替换原则
- 在继承上,尽量不要重写父类的方法
- 适当情况下,可以使用组合、聚合来代替继承
- 可以使用父类的地方也可以使用子类
- 开闭原则
OCP
- 开闭原则是最重要的原则
- 对扩展开发;对修改关闭。
- 对于增加服务的时候,不会影响已使用的代码;
- 迪米特法则
- 合成复用原则
设计模式目的
- 代码重用性
- 可读性
- 可扩展性
- 可靠性
- 使程序高内聚,低耦合
单一职责原则
说明
对类来说,一个类只负责一项职责。比如说A类有两个职责,职责1,职责2;那么就可以把A类分为两个类,这就是遵循了单一职责原则
代码演示的升级变化
/**
* @author HiWin10
* @date 2020/7/29
* 学习单一职责原则,使用交通工具来进行举例,
* 该版本类,是没有遵循单一职责原则的,应该这个类的公交类很复杂,使用了很多交通工具,解决办法是,根据交通工具的类别分别创建对应的类
*/ public class SingleResponsibility1 {
public static void main(String[] args) {
Vehicle vehicle = new Vehicle();
vehicle.run("摩托车");
vehicle.run("飞机");
vehicle.run("轮船");
}
}
/**
* 交通工具类
* 没有遵循单一职责原则,解决办法是,根据交通工具分别创建对应类 具体看SingleResponsibility2
*/ class Vehicle {
public void run (String vehicle) {
System.out.println(vehicle + "在公路上运行....");
}
}
/**
* @author HiWin10
* @date 2020/7/29
* 1. 遵守了单一职责原则,
* 2. 但是这样改动很大,将类分解,还要修改客户端
* 3. 直接修改Vehicle,对于咱们目前的场景,这个类只有一个功能的话,那么可以有方法单一职责具体看SingleResponsibility3,
* 当然SingleResponsibility3具体要看需求的
*/ public class SingleResponsibility2 {
public static void main(String[] args) {
RoadVehicle roadVehicle = new RoadVehicle();
roadVehicle.run("摩托");
AriVehicle ariVehicle = new AriVehicle();
ariVehicle.run("飞机");
WaterVehicle waterVehicle = new WaterVehicle();
waterVehicle.run("轮船");
}
}
class RoadVehicle {
public void run (String vehicle) {
System.out.println(vehicle + "在公路上运行....");
}
}
class AriVehicle {
public void run (String vehicle) {
System.out.println(vehicle + "在天空运行....");
}
}
class WaterVehicle {
public void run (String vehicle) {
System.out.println(vehicle + "在水里运行....");
}
}
/**
* @author HiWin10
* @date 2020/7/29
* 1. 这种方式没有对SingleResponsibility1而言没有大的修改,只是增加方法
* 2. 这种方式虽然对类没有遵循单一职责原则,但是在方法上,遵循了单一职业原则
* 3. 所以说即使是设计模式,也没有说必须的套路,要根据实际应用来使用
*/ public class SingleResponsibility3 {
public static void main(String[] args) {
Vehicle3 vehicle = new Vehicle3();
vehicle.run("摩托");
vehicle.runAir("飞机");
vehicle.runWater("轮船");
}
}
class Vehicle3 {
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 + "在水中运行....");
}
}
小结
- 降低类的复杂度,一个类只负责意见事情
- 提高代码的可读性和维护性,因为一个类只做一件事情,那么这个类可以说是很简单
- 修改其他有关代码,不会影响其他代码
- 通常情况下,我们应对遵循单一职责原则,只有在代码逻辑十分简单,可以不遵守或者可以在方法级别上遵守
接口隔离原则
概念
客户端类不应该依赖它不要的接口,既一个类对另一个类的依赖应该建立在最小接口上
简答的说就是不需要的类不要依赖,不要的方法不要实现。
- 该图片中,A类使用B类的
1,2,3方法;C类使用D类的1,4,5方法;B类和D类分别都实现了Interface1的所有方法。 - 那么根据接口隔离原则来讲,使用不到的不实现。也就是说B类不需要实现
Interface1的4,5方法,D类不要实现Interface1的2,3方法 - 解决办法:将
Interface1接口中方法分离,1方法单独一个接口,2,3单独一个接口,4,5单独一个接口 - 根据实际情况来定,这个接口隔离原则还可以使用抽象类来解决,或者普通的类也可以解决。
下面是没有遵循接口隔离原则的场景模拟
/**
* @author HiWin10
* @date 2020/7/31
* 接口隔离原则模拟
* 接口隔离就是一个接口有很多方法,每个实现都要排除不必要的方法
*/ public class Segregation1 {
public static void main(String[] args) {
A a = new A();
a.depend1(new B());
a.depend2(new B());
a.depend3(new B());
System.out.println("-----------------------");
C c = new C();
c.depend1(new D());
c.depend4(new D());
c.depend5(new D());
}
}
/**
* 接口有5个方法,全部都要实现
*/ interface Interface1 {
void operation1();
void operation2();
void operation3();
void operation4();
void operation5(); }
/**
* 接口的实现,B,但是A类只会依赖B类(使用),使用B类的1,2,3,方法
*/ class B implements Interface1{
@Override
public void operation1() {
System.out.println("B 实现了Interface1的 operation1方法 ");
}
@Override
public void operation2() {
System.out.println("B 实现了Interface1的 operation2方法 ");
}
@Override
public void operation3() {
System.out.println("B 实现了Interface1的 operation3方法 ");
}
@Override
public void operation4() {
System.out.println("B 实现了Interface1的 operation4方法 ");
}
@Override
public void operation5() {
System.out.println("B 实现了Interface1的 operation5方法 ");
}
}
/**
* 接口的实现D,但是C类只会依赖D类(使用),使用D类的1,4,5,方法
*/ class D implements Interface1 {
@Override
public void operation1() {
System.out.println("D 实现了Interface1的 operation1方法 ");
}
@Override
public void operation2() {
System.out.println("D 实现了Interface1的 operation2方法 ");
}
@Override
public void operation3() {
System.out.println("D 实现了Interface1的 operation3方法 ");
}
@Override
public void operation4() {
System.out.println("D 实现了Interface1的 operation4方法 ");
}
@Override
public void operation5() {
System.out.println("D 实现了Interface1的 operation5方法 ");
}
}
/**
* A类通过接口依赖B类(使用B类,只会使用1,2,3方法)
* 通过这个就可以看到,我只想使用接口的三个方法,但是B类却实现了Interface1接口的所有方法
* 这样的话对于A类来说4,5方法都是使用不到的,却实现了,从这里看代码就非常多余
*/ class A {
public void depend1(Interface1 i){
i.operation1();
}
public void depend2(Interface1 i){
i.operation2();
}
public void depend3(Interface1 i){
i.operation3();
}
}
class C { // C类通过接口依赖D类(使用D类,只会使用1,4,5方法)
public void depend1(Interface1 i){
i.operation1();
}
public void depend4(Interface1 i){
i.operation4();
}
public void depend5(Interface1 i){
i.operation5();
}
}
遵循接口隔离原则
package com.itcast.接口隔离原则; /**
* @author HiWin10
* @date 2020/7/31
* 接口隔离原则模拟
* 接口隔离就是一个接口有很多方法,每个实现都要排除不必要的方法
* 解决办法:
* 将几个方法根据使用不同来拆除,比如说A类使用1,2,3方法;C类使用1,4,5方法,那么1方法就是1个类,2,3一类,4,5一个类
* 也可以使用抽象类解决该问题
*/ public class Segregation2 {
public static void main(String[] args) {
A2 a = new A2();
a.depend1(new B2());
a.depend2(new B2());
a.depend3(new B2());
System.out.println("------------------");
C2 c = new C2();
c.depend1(new D2());
c.depend4(new D2());
c.depend5(new D2()); }
}
/**
* 将接口方法根据使用来拆除,后期实现可以根据实际情况使用抽象类解决该问题
*/ interface Interface2 {
void operation1(); }
interface Interface3 {
void operation2();
void operation3(); }
interface Interface4 {
void operation4();
void operation5(); }
class B2 implements Interface2, Interface3 {
@Override
public void operation1() {
System.out.println("B2 实现了 Interface2的operation1方法");
}
@Override
public void operation2() {
System.out.println("B2 实现了 Interface2的operation2方法");
}
@Override
public void operation3() {
System.out.println("B2 实现了 Interface2的operation3方法");
}
}
class D2 implements Interface2, Interface4 {
@Override
public void operation1() {
System.out.println("D2 实现了 Interface2的operation1方法");
}
@Override
public void operation4() {
System.out.println("D2 实现了 Interface2的operation4方法");
}
@Override
public void operation5() {
System.out.println("D2 实现了 Interface2的operation5方法");
}
}
class A2 {
public void depend1(Interface2 i){
i.operation1();
}
public void depend2(Interface3 i){
i.operation2();
}
public void depend3(Interface3 i){
i.operation3();
}
}
class C2 {
public void depend1(Interface2 i){
i.operation1();
}
public void depend4(Interface4 i){
i.operation4();
}
public void depend5(Interface4 i){
i.operation5();
}
}
依赖倒转原则
- 高层模块不要依赖底层模块,二者都要依赖其抽象
- 抽象不要依赖细节,细节应该依赖抽象
- 依赖倒转(倒置)的中心思想就是面向接口编程
- 依赖倒转原则基于这样的设计理念;对于细节的多边形,抽象要稳定的多。以抽象为基础搭建的架构要比以细节为基础的架构稳定的多。在Java中抽象就是接口或抽象类,细节就是实体类
- 使用接口或抽象类目的是指定好的规范,而不涉及任何具体操作。把展示细节部分交给他们的实现类完成
不遵循依赖倒转原则的代码
/**
* @author HiWin10
* @date 2020/7/31
* 依赖倒转原则
* 1. 高层模块不要依赖底层模块,二者都要依赖其抽象
* 2. 抽象不要依赖细节,细节应该依赖抽象
* 3. 依赖倒转(倒置)的中心思想就是面向接口编程
* 4. 依赖倒转原则基于这样的设计理念;对于细节的多边形,抽象要稳定的多。
* 以抽象为基础搭建的架构要比以细节为基础的架构稳定的多。在Java中抽象就是接口或抽象类,细节就是实体类
* 5. 使用接口或抽象类目的是指定好的规范,而不涉及任何具体操作。把展示细节部分交给他们的实现类完成
*
* 案例:就是接受消息的一个案例
*/ public class DependecyInversion {
public static void main(String[] args) {
Person person = new Person();
person.receive(new Email());
}
}
/**
* 消息类
*/ class Email {
String getInfo(){
return "邮箱消息:Hello, World!";
}
}
/**
* 分析:
* 1. 简单,比较容易想到
* 2. 缺乏灵活性,如果我们发送Person类的消息不是Email的了,而是微信消息那么该如何操作?这种写法的话,代码写死了缺乏灵活性
* 解决思路:中间定义一个抽象层,方法参数为抽象层,实体类实现了抽象层,这样来调用就会灵活的多
*/ class Person{
public void receive(Email email){
System.out.println(email.getInfo());
}
}
遵循依赖倒转原则
/**
* @author HiWin10
* @date 2020/7/31 */ public class DependecyInversion_OK {
public static void main(String[] args) {
Person1 person = new Person1();
// 发送电子邮箱消息
person.receive(new Email1()); // 发送微信消息
person.receive(new Weixin()); }
}
interface IReceive {
public String getInfo(); }
/**
* 电子邮件消息类
*/ class Email1 implements IReceive {
@Override
public String getInfo(){
return "邮箱消息:Hello, World!";
}
}
/**
* 微笑消息类
*/ class Weixin implements IReceive {
@Override
public String getInfo() {
return "微信消息:Are you OK";
}
}
class Person1{
public void receive(IReceive receive){
System.out.println(receive.getInfo());
}
}
依赖倒转原则关系传递的三种方式
- 构造器传递
setter方法传递- 方法接口传递
代码示例
/**
* @author HiWin10
* @date 2020/7/31
* 依赖倒转原则三种传递方式
*/ public class DependencyPass {
}
// 方式1:通过接口传递 // 开关的接口 interface IOpenAndClose {
public void open(ITV itv); // 抽象方法,接受接口 }
/**
* 接口
*/ interface ITV {
public void play(); }
/**
* 电视的实现
*/ class ChangHong implements ITV {
@Override
public void play() {
System.out.println("长虹电视开机了...");
}
}
/**
* 操作类的实现
*/ class OpenAndClose implements IOpenAndClose {
@Override
public void open(ITV itv) {
itv.play();
}
}
/*******************/ // 方式2:构造器传递 interface IOpenAndClose2 {
public void open(ITV itv); // 抽象方法,接受接口 }
/**
* 接口
*/ interface ITV2 {
public void play(); }
class ChangHong2 implements ITV2 {
@Override
public void play() {
System.out.println("长虹电视开机了...");
}
}
/**
* 构造器方式
*/ class OpenAndClose2 implements IOpenAndClose2 {
private ITV2 itv2; public OpenAndClose2(ITV2 itv2) {
this.itv2 = itv2;
}
@Override
public void open(ITV itv) {
}
}
/*******************/ // 方式3:set方法 interface IOpenAndClose3 {
public void open(ITV itv); // 抽象方法,接受接口 }
/**
* 接口
*/ interface ITV3 {
public void play(); }
class ChangHong3 implements ITV3 {
@Override
public void play() {
System.out.println("长虹电视开机了...");
}
}
/**
* set方法
*/ class OpenAndClose3 implements IOpenAndClose3 {
private ITV3 itv3; public void setItv3(ITV3 itv3) {
this.itv3 = itv3;
}
@Override
public void open(ITV itv) {
this.itv3.play();
}
}
小结
- 底层模块尽量都要有抽象或接口。或者两者都有,程序稳定性会更好
- 变量的声明类型尽量是抽象类或接口,这样我们的变量引用和实际对象间,就存在一个缓冲层,利于程序扩展和优化
- 继承时遵循里氏替换原则
里氏替换原则
继承带来的优劣势
-
继承包含这样一个含义:父类中凡是已经实现好的方法,实际上是在设定规范和契约,虽然没有强制所有子类必须遵循这些契约,但是如果子类随意重写父类实现的方法,就会对继承体系造成破坏
-
继承在给程序带来便利同时,也会有弊端。比如使用继承会给程序带来侵入性,程序的可移植性减低,增加对象的耦合,如果一个类被其他类所继承,则这个类需要修改时,必须考虑所有子类,并且父类修改后,所有涉及到子类的功能都有可能产生故障
-
问题提示:在编程中,如何正确的使用继承?=> 里氏替换原则
里氏替换原则概念
- 如果对每个类型为T1的对象o1,都有类型为
T2的对象o2,使得以T1定义的所有程序P在所有对象o1都代还成o2时,程序P的行为没有发生变化,那么T2是类型T1的子类。或句话说,所有引用基类的地方必须能透明地使用其子类对象 - 使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法
- 里氏替换原则告诉我们,继承实际上让两个类的耦合提高了,在适当情况下,可以通过聚合、组合、依赖来解决问题
问题产出
/**
* @author HiWin10
* @date 2020/8/3
* 里氏替换原则
* 里氏替换原则需要满足一下几点:
* 1. 所有使用基类的地方都可以使用子类来代替
* 2. 尽量不要重写父类的方法
* 3. 在适当情况下,可以选择使用聚合、组合、依赖来解决问题
*/ public class Liskow1 {
public static void main(String[] args) {
A a = new A();
System.out.println("20-10=" + a.func(20, 10));
System.out.println("20-18=" + a.func(20, 18)); System.out.println("------------");
B b = new B();
System.out.println("11-3=" + b.func(10, 20));
System.out.println("20-18+1=" + b.fun2(20, 18)); }
}
class A {
/**
* 减法
*/
public Integer func(int a, int b){
return a - b;
}
}
// 按照main中B对象的逻辑,是想要使用A的方法,但是B类不小心重写了A的其中方法,所以说才会发生此事,计算失误。违反了里氏替换原则 // 解决办法:使用聚合、组合、依赖来解决问题 class B extends A {
/**
* B类不小心重写了 A类的方法
*/
public Integer func(int a, int b) {
return a + b;
}
public Integer fun2(int a, int b) {
return func(a, b) + 1;
}
}
- A类有一个方法
func作用减法。 - B类继承A类想要在
main中使用func但是不小心重写了A类的func方法,所以在使用的时候逻辑上不正确 - 解决方法就是使用使用聚合、组合、依赖来解决问题
代码如下:
/**
* @author HiWin10
* @date 2020/8/3
* 里氏替换原则
* 里氏替换原则需要满足一下几点:
* 1. 所有使用基类的地方都可以使用子类来代替
* 2. 尽量不要重写父类的方法
* 3. 在适当情况下,可以选择使用聚合、组合、依赖来解决问题
*/ public class Liskow2 {
public static void main(String[] args) {
A1 a = new A1();
System.out.println("20-10=" + a.func(20, 10));
System.out.println("20-18=" + a.func(20, 18)); System.out.println("------------");
B1 b = new B1();
System.out.println("11-3=" + b.func(11, 3));
System.out.println("20-18+1=" + b.fun2(20, 18)); }
}
class A1 {
/**
* 减法
*/
public Integer func(int a, int b){
return a - b;
}
}
// 按照main中B对象的逻辑,是想要使用A的方法,但是B类不小心重写了A的其中方法,所以说才会发生此事,计算失误。违反了里氏替换原则 // 解决办法:使用聚合、组合、依赖来解决问题/本次使用组合来解决 class B1 {
private A1 a = new A1(); /**
* B类不小心重写了 A类的方法
*/
public Integer func(int a, int b) {
// return a + b;
return this.a.func(a, b);
}
public Integer fun2(int a, int b) {
return this.a.func(a, b) + 1;
}
}
开闭原则[最重要]
概念
- 开闭原则是编程中最基础、最重要的原则
- 一个软件实体如类,模块和函数应该对扩展开发(对提供方),对修改关闭(对使用方)。由抽象构建框架,用实现扩展细节。
- 当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有代码实现变化。
- 编程中遵循其他原则,以及使用设计模式的目的就是遵循开闭原则
代码示例
该代码是,通过方法来决定绘画那种图形,这里是没有遵循开闭原则的,你会发现,再添加一个图形的时候,不管是服务方还是使用方都要修改或扩展代码
/**
* @author HiWin10
* @date 2020/8/3
* 开闭原则
* 1. 对功能扩展开放,对修改关闭
* 2. 尽量使用软件的实体行为实现变化,而不是修改已有代码
*/ public class Ocp1 {
public static void main(String[] args) {
GraphicEditor graphicEditor = new GraphicEditor();
graphicEditor.drawShape(new Rectangle());
graphicEditor.drawShape(new Circle()); // 绘画新添加的三角形
graphicEditor.drawShape(new Triangle());
}
}
// 没有遵循开闭原则,当要添加一个图形的时候,要修改的东西太多, // 首先,服务需要修改 其次使用方也要修改代码 // [使用方] // 解决办法:添加抽象层,添加好规范好,由具体实现来完成具体图形的绘画
class GraphicEditor {
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){
drawTriangle(s);
}
}
// 绘制矩形
private void drawRectangle(Shape s) {
System.out.println(" 绘制矩形 ");
}
// 绘制圆形
private void drawCircle(Shape s) {
System.out.println(" 绘制圆型 ");
}
// 这里修改添加新方法drawTriangle画三角形
private void drawTriangle(Shape s){
System.out.println("绘制 三角形");
}
}
class Shape {
int m_type; }
// 矩形 class Rectangle extends Shape {
public Rectangle() {
super.m_type = 1;
}
}
// 圆形 class Circle extends Shape {
public Circle() {
super.m_type = 2;
}
}
// 新添加三角形 [服务方] class Triangle extends Shape {
public Triangle() {
super.m_type = 3;
}
}
问题
- 虽然对提供方是扩展(新增类),对使用方还是修改
- 解决思路:中间添加抽象层,然后图形的绘画交给具体实现,而不是由使用方来绘画
/**
* @author HiWin10
* @date 2020/8/3
* 开闭原则
* 1. 对功能扩展开放,对修改关闭
* 2. 尽量使用软件的实体行为实现变化,而不是修改已有代码
*
* 开始使用开闭原则
* 添加抽象类,定义接口 然后每个实现来绘画对象图形
*/ public class Ocp2 {
public static void main(String[] args) {
GraphicEditor1 graphicEditor = new GraphicEditor1();
graphicEditor.drawShape(new Rectangle1());
graphicEditor.drawShape(new Circle1()); // 绘画新添加的三角形
graphicEditor.drawShape(new Triangle1());
}
}
class GraphicEditor1 {
public void drawShape(Shape2 s){
s.drawShape();
}
}
abstract class Shape2 {
int m_type;
public abstract void drawShape(); }
// 矩形 class Rectangle1 extends Shape2 {
public Rectangle1() {
super.m_type = 1;
}
@Override
public void drawShape() {
System.out.println(" 绘制矩形 ");
}
}
// 圆形 class Circle1 extends Shape2 {
public Circle1() {
super.m_type = 2;
}
@Override
public void drawShape() {
System.out.println(" 绘制圆型 ");
}
}
// 新添加三角形 class Triangle1 extends Shape2 {
public Triangle1() {
super.m_type = 3;
}
@Override
public void drawShape() {
System.out.println("绘制 三角形");
}
}
小结
以后在设计程序尽量使用抽象,这样的话程序会非常灵活。但是同样的程序也会非常负责。
迪米特法则
概念
- 一个对象应该对其他对象保持最少了解
- 类与类的关系越密切,耦合度越高
- 迪米特法则,又叫最少知道原则。既一个类对自己依赖的类知道的越少越好。也就是说。对于依赖的类不管多么复杂,都要尽量将逻辑封装在类的内部,对外除了提供的
public方法,不对外泄露任何信息 - 迪米特法则还有更简单的定义:只与直接的朋友通信
- 直接朋友:每个对象都会与其他对象有耦合关系,只要连个对象之间有耦合关系,我们就说这两个对象是朋友关系。我们称出现成员变量、方法参数、方法返回值就是直接朋友,而在局部出现的类就不是直接朋友。也就是说,陌生的类最好不要以局部变量方式存在类中
耦合关系分别有:依赖,关联,组合,聚合等。
代码示例
- 有一个学校,下属有各个学院和总部,现要求打印出学校总部员工ID和学院员工ID
- 代码演示
/**
* @author HiWin10
* @date 2020/8/4
* 迪米特法则
* 1. 一个对象应该对其他对象保持最少的了解
* 2. 类与类关系密切,耦合度就越高
* 3. 迪米特法则又叫最少知道原则,既一个类对自己依赖的类知道越少越好,也就是说,对被依赖的类不管多么复杂,都要尽量将逻辑封装在类内部,对外提供public方法,不对外泄露
* 4. 迪米特法则还有更简单定义:只与直接朋友通信
*
* 直接朋友:该类的属性,或者setter方法;或者方法参数
*/ public class Demeter1 {
public static void main(String[] args) {
SchoolManager schoolManager = new SchoolManager();
schoolManager.printAllEmployee(new CollegeManager());
}
}
// 学院总员工类 class Employee {
private String id; public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Override
public String toString() {
return "Employee{" +
"id='" + id + '\'' +
'}';
}
}
// 学院的员工类 class CollegeEmployee {
private String id; public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Override
public String toString() {
return "CollegeEmployee{" +
"id='" + id + '\'' +
'}';
}
}
// 管理学院员工的管理类 class CollegeManager {
// 返回学院所有员工
public List<CollegeEmployee> getAllEmployee() {
List<CollegeEmployee> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
CollegeEmployee emp = new CollegeEmployee();
emp.setId("学院员工id = " + i);
list.add(emp);
}
return list;
}
}
// 学院管理类 // 分析:该类的直接朋友:Employee、CollegeManager // CollegeEmployee 类不是直接朋友违背了迪米特法则。将这一段代码移动到该类实现上去 class SchoolManager {
// 返回学院总部的员工
public List<Employee> getAllEmployee() {
List<Employee> list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
Employee emp = new Employee();
emp.setId("学院总员工ID = " + i);
list.add(emp);
}
return list;
}
// 该方法返回输出学院总部和学院员工信息
public void printAllEmployee(CollegeManager sub) {
// 获取到学院员工
// 解决办法:将这一段代码移动到该类实现上去,
List<CollegeEmployee> list = sub.getAllEmployee();
System.out.println("-------学院员工-------");
list.forEach(System.out::println); // 获取学院总部员工
List<Employee> employeeList = this.getAllEmployee();
System.out.println("-------学院总员工-------");
employeeList.forEach(System.out::println);
}
}
问题发现
- CollegeEmployee 类不是直接朋友违背了迪米特法则。将这一段代码移动到该类实现上
/**
* @author HiWin10
* @date 2020/8/4
* 迪米特法则
* 1. 一个对象应该对其他对象保持最少的了解
* 2. 类与类关系密切,耦合度就越高
* 3. 迪米特法则又叫最少知道原则,既一个类对自己依赖的类知道越少越好,也就是说,对被依赖的类不管多么复杂,都要尽量将逻辑封装在类内部,对外提供public方法,不对外泄露
* 4. 迪米特法则还有更简单定义:只与直接朋友通信
*
* 直接朋友:该类的属性,或者setter方法;或者方法参数
*/ public class Demeter2 {
public static void main(String[] args) {
SchoolManager2 schoolManager = new SchoolManager2();
schoolManager.printAllEmployee(new CollegeManager2());
}
}
// 学院总员工类 class Employee2 {
private String id; public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Override
public String toString() {
return "Employee{" +
"id='" + id + '\'' +
'}';
}
}
// 学院的员工类 class CollegeEmployee2 {
private String id; public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Override
public String toString() {
return "CollegeEmployee{" +
"id='" + id + '\'' +
'}';
}
}
// 管理学院员工的管理类 class CollegeManager2 {
// 返回学院所有员工
public List<CollegeEmployee2> getAllEmployee() {
List<CollegeEmployee2> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
CollegeEmployee2 emp = new CollegeEmployee2();
emp.setId("学院员工id = " + i);
list.add(emp);
}
return list;
}
// 显示移动到改方法中
public void printAllemployee() {
List<CollegeEmployee2> list = this.getAllEmployee();
System.out.println("-------学院员工-------");
list.forEach(System.out::println);
}
}
// 学院管理类 // 分析:该类的直接朋友:Employee、CollegeManager // CollegeEmployee 类不是直接朋友违背了迪米特法则。将这一段代码移动到该类实现上去 class SchoolManager2 {
// 返回学院总部的员工
public List<Employee2> getAllEmployee() {
List<Employee2> list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
Employee2 emp = new Employee2();
emp.setId("学院总员工ID = " + i);
list.add(emp);
}
return list;
}
// 该方法返回输出学院总部和学院员工信息
public void printAllEmployee(CollegeManager2 sub) {
// 获取到学院员工
// 解决办法:将这一段代码移动到该类实现上去,
sub.printAllemployee(); // 获取学院总部员工
List<Employee2> employeeList = this.getAllEmployee();
System.out.println("-------学院总员工-------");
employeeList.forEach(System.out::println);
}
}
合成复用原则
就是尽量以合成/聚合的方式,而不是使用继承
设计模式总结
- 找到应用中可能要变化之处,把他们独立出来,不要和那些不需要变化的代码混在一起
- 针对接口编程。而不是针对实现编程
- 为了交互对象之间松耦合设计而努力



浙公网安备 33010602011771号