设计模式概述
设计模式是在软件开发中经过无数编程先辈在软件开发中的血泪教训总结出的一套用于解决问题的最佳方案。
1995 年,GoF(Gang of Four,四人组/四人帮)合作出版了《设计模式:可复用面向对象软件的基础》一书,共收录了 23 种设计模式,从此树立了软件设计模式领域的里程碑,人称「GoF设计模式」。
这 23 种设计模式的本质是面向对象设计原则的实际运用,是对类的封装性、继承性和多态性,以及类的关联关系和组合关系的充分理解。
接地气地说:我们学习设计模式后的作用有两个:其一是应对各种公司特别是一线互联网公司的面试;其二是能够在工作中运用到自己开发的软件中使软件的品质达到很高的水平。
使用设计模式的目的:
- 代码复用性:即相同功能的代码只用写一遍不用重复编写
- 可读性:编写的代码易于理解,遵循了统一的规范
- 可扩展性(也叫可维护性):当需要增加和扩展新的功能的时候能够很容易的去扩展,扩展新功能成本低
- 可靠性:增加的新功能不会影响到旧的功能的使用
- 最终达到:软件的高内聚,低耦合的状态
设计模式七大基本原则
Java包含23中设计模式,这23种设计模式都是分别遵循了这七大基本原则而进行的。
开闭原则
开闭原则是编程中最基础、最核心的原则。就是指类或者模块或者软件应该对扩展开放,对修改关闭。用抽象去搭建框架,用实现去扩展细节,当软件功能需要发生变化时,应该使用扩展的方式去应对变化,而不是去修改原有的逻辑去应对变化。
反例:
这种就是典型地违反了设计模式的开闭原则的例子,在本例中如果要新增新的类型的机动车的生产,则需要去修改大量的使用方的逻辑和新增提供方的逻辑。开闭原则要求是在应对新的需求产生的变更时应该尽量不修改代码或者少修改代码,特别是对于使用方来说应该尽可能完全不修改代码,比如在本例中我们要新增加生产拖拉机和生产中巴车的功能,需要修改的地方很多,特别是在使用方的修改量特别大。
java
/**
* 开闭原则-反例
*/
public class OpeningAndClosingPrincipleDemo1 {
public static void main(String[] args) {
//创建机动车生产者类对象
MotorVehicleProduction mvp = new MotorVehicleProduction();
//生产一辆大巴车
mvp.product(new Bus());
//生产一辆小轿车
mvp.product(new SedanCar());
//生产一辆拖拉机
mvp.product(new Tractor());
//生产一辆中巴车
mvp.product(new MiddleBus());
}
}
/**
* 机动车生产者类(使用方)
*/
class MotorVehicleProduction {
/**
* 接收一个机动车对象,然后根据类型去决定生产哪种具体的机动车
*/
public void product(MotorVehicle mv) {
if (mv.type == 1) {
productBus(mv);
} else if (mv.type == 2) {
productSedanCar(mv);
} else if (mv.type == 3) {
productTractor(mv);
} else if (mv.type == 4) {
productTractor(mv);
}
}
/**
* 生产大巴车
*/
public void productBus(MotorVehicle mv) {
System.out.println("生产" + mv.name + "......");
}
/**
* 生产小轿车
*/
public void productSedanCar(MotorVehicle mv) {
System.out.println("生产" + mv.name + "......");
}
/**
* 生产拖拉机
*/
public void productTractor(MotorVehicle mv) {
System.out.println("生产" + mv.name + "......");
}
/**
* 生产中巴车
*/
public void productMiddleBus(MotorVehicle mv) {
System.out.println("生产" + mv.name + "......");
}
}
/**
* 机动车类
*/
class MotorVehicle {
protected int type;
protected String name;
}
/**
* 大巴车类
*/
class Bus extends MotorVehicle {
Bus() {
super.type = 1;
super.name = "大巴车";
}
}
/**
* 小轿车类
*/
class SedanCar extends MotorVehicle {
SedanCar() {
super.type = 2;
super.name = "小轿车";
}
}
/**
* 拖拉机类
*/
class Tractor extends MotorVehicle {
Tractor() {
super.type = 3;
super.name = "拖拉机";
}
}
/**
* 中巴车类
*/
class MiddleBus extends MotorVehicle {
MiddleBus() {
super.type = 4;
super.name = "中巴车";
}
}
正例:
改进思路:将机动车类设计为抽象类,并提供一个生产机动车的抽象方法,让子类去实现即可,这样我们新增功能时就只需要让新的类去继承机动车类并重写生产机动车的方法即可。这样就不需要修改使用方的代码,满足了开闭原则。
java
/**
* 开闭原则-正例
*/
public class OpeningAndClosingPrincipleDemo2 {
public static void main(String[] args) {
//创建机动车生产者类对象
MotorVehicleProduction1 mvp = new MotorVehicleProduction1();
//生产一辆大巴车
mvp.product(new Bus1());
//生产一辆小轿车
mvp.product(new SedanCar1());
//生产一辆拖拉机
mvp.product(new Tractor1());
//生产一辆中巴车
mvp.product(new MiddleBus1());
}
}
/**
* 机动车生产者类(使用方)
*/
class MotorVehicleProduction1 {
public void product(MotorVehicle1 mv) {
/*
*1、避免在类中使用else if......
*2、避免增加新功能和需求有变更的时候去修改源代码
*/
mv.product();
}
}
/**
* 机动车类
*/
abstract class MotorVehicle1 {
protected int type;
protected String name;
/**
* 抽象方法
*/
protected abstract void product();
}
/**
* 大巴车类
*/
class Bus1 extends MotorVehicle1 {
Bus1() {
super.type = 1;
super.name = "大巴车";
}
@Override
protected void product() {
System.out.println("生产" + this.name + "......");
}
}
/**
* 小轿车类
*/
class SedanCar1 extends MotorVehicle1 {
SedanCar1() {
super.type = 2;
super.name = "小轿车";
}
@Override
protected void product() {
System.out.println("生产" + this.name + "......");
}
}
/**
* 拖拉机类
*/
class Tractor1 extends MotorVehicle1 {
Tractor1() {
super.type = 3;
super.name = "拖拉机";
}
@Override
protected void product() {
System.out.println("生产" + this.name + "......");
}
}
/**
* 中巴车类
*/
class MiddleBus1 extends MotorVehicle1 {
MiddleBus1() {
super.type = 4;
super.name = "中巴车";
}
@Override
protected void product() {
System.out.println("生产" + this.name + "......");
}
}
单一职能原则
单一职能原则指的就是一个类或者一个方法只具有一项职能,不应具有多项职能。如果具有多项职能会导致要修改起来或者变化起来的成本非常高,非常麻烦。单一职能原则有以下几个优点:
- 单一职能原则的目的主要是降低类的复杂度,即一个类或者一个方法只负责一项职能,与其无关的职能工作不要冗余地写到一个这个类或者这个方法中。
- 提高类的可读性和可维护性。
- 降低了变更带来的不稳定风险。
- 通常情况下应该在类的层面遵守单一职能原则,只有在类中的方法数量足够少逻辑足够简单的情况下才保持在方层面遵守单一职能原则。
反例:
以老师教授知识为例说明单一职能原则,在teach方法中就是典型地违反了设计模式的单一职能原则的例子
java
/**
* 单一职能原则-反例
*/
public class SingleFunctionPrincipleDemo1 {
public static void main(String[] args) {
Teacher teacher = new Teacher();
teacher.teach("汉语拼音和识字");
teacher.teach("加减乘除法");
teacher.teach("微积分");
}
}
/**
* 老师类
*/
class Teacher {
public void teach(String content) {
System.out.println("教授小学生:" + content);
}
}
正例一:
改进思路:根据不同年龄段的学生接受知识的不同,将老师类进行分解即可。
这样设计是在类的层面遵守了单一职能原则,但是改动量较大,需要将类拆解,并修改客户端,这样就既修改了提供方又修改了使用方。
java
/**
* 单一职能原则-正例一
*/
public class SingleFunctionPrincipleDemo2 {
public static void main(String[] args) {
PrimaryTeacher primaryTeacher = new PrimaryTeacher();
primaryTeacher.teach("汉语拼音和识字");
primaryTeacher.teach("加减乘除法");
UniversityTeacher universityTeacher = new UniversityTeacher();
universityTeacher.teach("微积分");
}
}
/**
* 小学老师类
*/
class PrimaryTeacher {
public void teach(String content) {
System.out.println("教授小学生知识中的:" + content);
}
}
/**
* 大学老师类
*/
class UniversityTeacher {
public void teach(String content) {
System.out.println("教授大学生知识中的:" + content);
}
}
正例二:
改进思路:直接修改Teacher类这样改动的代码就会比较少。
虽然在类的层面没有遵守单一职能原则,但是在方法的层面遵守了单一职能原则,简化了修改代码的成本。
类似我们的UserService类具有:insert、update、delete、select多种方法,UserService即操作User表又操作其他表数据,因为UserService是用户服务类,它包含了对用户的所有关联的操作
java
/**
* 单一职能原则-正例二
*/
public class SingleFunctionPrincipleDemo3 {
public static void main(String[] args) {
Teacher1 teacher = new Teacher1();
teacher.teachPrimary("汉语拼音和识字");
teacher.teachSenior("高中物理力学");
teacher.teachUniversity("微积分");
}
}
/**
* 老师类
*/
class Teacher1 {
public void teachPrimary(String content) {
System.out.println("教授小学生知识中的:" + content);
}
public void teachSenior(String content) {
System.out.println("教授高中生知识中的:" + content);
}
public void teachUniversity(String content) {
System.out.println("教授大学生知识中的:" + content);
}
}
里氏替换原则
里氏替换原则是面向对象编程中对继承的一种规范和要求。就是子类不要去重写父类已经实现的方法。极端例子:如果子类全部重写了父类的方法,那么子类继承父类这个操作就毫无意义。如果一个类需要使用到另一个类的功能,在合理的情况下可以使用继承的方式去设计实现,但是要考虑耦合度是否达到要求,如果要降低耦合度可以使用组合、聚合、依赖的方式去解决,如果是使用继承那么要遵循里氏替换原则。
反例:
由于Cat类继承了Dog类,所以创建Cat类的对象后调用方法,就会以为是调用的是Dog类中的方法,但是这里显然不是这样。
java
/**
* 里氏替换原则-反例
*/
public class RichterSubstitutionPrincipleDemo01 {
public static void main(String[] args) {
System.out.println("Dog对象执行方法:------------------------------------");
Dog dog = new Dog();
dog.action();
System.out.println("Cat对象执行方法:------------------------------------");
Dog dog2 = new Cat();
dog2.action();
}
}
/**
* 狗类
*/
class Dog {
/**
* 活动方式
*/
public void action() {
System.out.println("不会爬树,白天活动,晚上不活动!");
}
}
/**
* 猫类
*/
class Cat extends Dog {
@Override
public void action() {
System.out.println("会爬树,晚上活动,白天不活动!");
}
public void cry() {
System.out.println("喵!喵!喵!");
}
}
正例:
以下就是符合继承规范,没有违背里氏替换原则。继承的更多目的是为了代码的重用,而不是一味的进行不合理的重写。
java
/**
* 里氏替换原则-反例
*/
public class RichterSubstitutionPrincipleDemo02 {
public static void main(String[] args) {
System.out.println("Dog对象执行方法:------------------------------------");
Dog1 dog = new Dog1();
dog.action();
System.out.println("SamoyeDog对象执行方法:------------------------------------");
SamoyeDog samoyeDog = new SamoyeDog();
samoyeDog.action();
samoyeDog.cry();
}
}
/**
* 狗类
*/
class Dog1 {
/**
* 活动方式
*/
public void action() {
System.out.println("不会爬树,白天活动,晚上不活动!");
}
}
/**
* 萨摩耶狗类
*/
class SamoyeDog extends Dog1 {
public void cry() {
System.out.println("汪!汪!汪!");
}
}
依赖倒置原则
依赖倒置原则就是高层模块不应该依赖于低层模块,二者都应该依赖其抽象。其核心就是面向接口编程。
反例:
以电脑接入设备为例说明依赖倒置原则,可以实现接入鼠标的功能,但是如果是后续扩展需要接入键盘、音响、麦克风等等呢?
则需要新增类,并且还要大量修改使用方Computer类中的去新增很多方法去实现。
java
/**
* 依赖倒置原则-反例
*/
public class DependencyInversionPrincipleDemo01 {
public static void main(String[] args) {
Computer computer = new Computer();
computer.access(new Mouse());
computer.access(new Keyboard());
computer.access(new Sound());
}
}
/**
* 电脑类
*/
class Computer {
/**
* 接入设备方法
*/
public void access(Mouse mouse) {
System.out.println("电脑上:" + mouse.afterAccess());
}
/**
* 接入设备方法
*/
public void access(Keyboard k) {
System.out.println("电脑上:" + k.afterAccess());
}
/**
* 接入设备方法
*/
public void access(Sound s) {
System.out.println("电脑上:" + s.afterAccess());
}
}
/**
* 鼠标类
*/
class Mouse {
public String afterAccess() {
return "鼠标已接入";
}
}
/**
* 键盘类
*/
class Keyboard {
public String afterAccess() {
return "键盘已接入";
}
}
/**
* 音响类
*/
class Sound {
public String afterAccess() {
return "音响已接入";
}
}
正例:
改进思路:引入一个抽象的接口USB接口去统一表示所有接入设备,使Computer类与USB接口发生依赖,这样使用方Computer类无需做任何修改就可以达到扩展的目的。
java
/**
* 依赖倒置原则-正例
*/
public class DependencyInversionPrincipleDemo02 {
public static void main(String[] args) {
Computer1 computer = new Computer1();
computer.access(new Mouse1());
computer.access(new Keyboard1());
computer.access(new Sound1());
}
}
/**
* 电脑类
*/
class Computer1 {
/**
* 接入设备方法
*/
public void access(USB usb) {
System.out.println("电脑上:" + usb.afterAccess());
}
}
interface USB {
/**
* 接入方法
*/
String afterAccess();
}
/**
* 鼠标类
*/
class Mouse1 implements USB {
public String afterAccess() {
return "鼠标已接入";
}
}
/**
* 键盘类
*/
class Keyboard1 implements USB {
public String afterAccess() {
return "键盘已接入";
}
}
/**
* 音响类
*/
class Sound1 implements USB {
public String afterAccess() {
return "音响已接入";
}
}
接口隔离原则
接口隔离原则就是一个类对另一个类的依赖应建立在最小接口上。通俗来讲就是一个客户端类使用到的一个接口引用的对象,应该使用到其接口的所有方法,如果没有完全使用到,那么就是这个接口不是最小接口,这样的情况就没有遵守接口隔离原则。
在一个客户端类中使用到了一个接口引用的对象的某些方法但是没有全部使用到这个接口中的所有方法,但是这个接口的实现类就必须去实现其所有的抽象方法,这样就导致了实现类在客户端类中没有使用到的方法是冗余的。
反例:
java
/**
* 接口隔离原则-反例
*/
public class InterfaceIsolationDemo01 {
public static void main(String[] args) {
//保安队长给保安安排任务
SafetyCaptain safetyCaptain = new SafetyCaptain();
safetyCaptain.makeCompanySafety(new SafetyKeeper());
//项目经理给程序员安排任务
ProjectManager projectManager = new ProjectManager();
projectManager.arrangeProgrammingTask(new Coder());
}
}
/**
* 互联网公司员工接口
*/
interface Employee {
/**
* 能打
*/
void canFighting();
/**
* 长得凶神恶煞
*/
void looksFierce();
/**
* 会编程
*/
void canProgramming();
/**
* 取得高中学历
*/
void canGetHighSchoolEducation();
}
/**
* 互联网公司保安员工
*/
class SafetyKeeper implements Employee {
public void canFighting() {
System.out.println("保安能打!经常叫嚣:我要打十个!");
}
public void looksFierce() {
System.out.println("保安长得凶神恶煞!能够震慑宵小之徒!");
}
public void canProgramming() {
System.out.println("保安会运用Java语言进行编程!");
}
public void canGetHighSchoolEducation() {
System.out.println("保安取得高中学历具有一定的逻辑思维");
}
}
/**
* 互联网公司程序员员工
*/
class Coder implements Employee {
public void canFighting() {
System.out.println("程序员能打!经常叫嚣:我要打十个!");
}
public void looksFierce() {
System.out.println("程序员长得凶神恶煞!能够震慑宵小之徒!");
}
public void canProgramming() {
System.out.println("程序员会运用Java语言进行编程!");
}
public void canGetHighSchoolEducation() {
System.out.println("程序员取得高中学历具有一定的逻辑思维");
}
}
/**
* 互联网公司项目经理
*/
class ProjectManager {
/**
* 安排编程任务
*/
public void arrangeProgrammingTask(Employee emp) {
emp.canGetHighSchoolEducation();
emp.canProgramming();
}
}
/**
* 互联网公司保安队长
*/
class SafetyCaptain {
/**
* 确保公司安全
*/
public void makeCompanySafety(Employee emp) {
emp.looksFierce();
emp.canFighting();
}
}
正例:
改进思路:将Employee接口拆分为几个独立功能的接口,Coder类和SafetyKeeper类分别与他们需要的接口进行依赖即可。这样就遵守了接口隔离原则。
java
/**
* 接口隔离原则-正例
*/
public class InterfaceIsolationDemo02 {
public static void main(String[] args) {
//保安队长给保安安排任务
SafetyCaptain1 safetyCaptain = new SafetyCaptain1();
safetyCaptain.makeCompanySafety(new SafetyKeeper1());
//项目经理给程序员安排任务
ProjectManager1 projectManager = new ProjectManager1();
projectManager.arrangeProgrammingTask(new Coder1());
}
}
/**
* 互联网公司保安员工接口
*/
interface SafetyEmployee {
/**
* 能打
*/
void canFighting();
/**
* 长得凶神恶煞
*/
void looksFierce();
}
/**
* 互联网公司程序员员工接口
*/
interface CoderEmployee {
/**
* 会编程
*/
void canProgramming();
/**
* 取得高中学历
*/
void canGetHighSchoolEducation();
}
/**
* 互联网公司保安员工
*/
class SafetyKeeper1 implements SafetyEmployee {
public void canFighting() {
System.out.println("保安能打!经常叫嚣:我要打十个!");
}
public void looksFierce() {
System.out.println("保安长得凶神恶煞!能够震慑宵小之徒!");
}
}
/**
* 互联网公司程序员员工
*/
class Coder1 implements CoderEmployee {
public void canProgramming() {
System.out.println("程序员会运用Java语言进行编程!");
}
public void canGetHighSchoolEducation() {
System.out.println("程序员取得高中学历具有一定的逻辑思维");
}
}
/**
* 互联网公司项目经理
*/
class ProjectManager1 {
/**
* 安排编程任务
*/
public void arrangeProgrammingTask(CoderEmployee emp) {
emp.canGetHighSchoolEducation();
emp.canProgramming();
}
}
/**
* 互联网公司保安队长
*/
class SafetyCaptain1 {
/**
* 确保公司安全
*/
public void makeCompanySafety(SafetyEmployee emp) {
emp.looksFierce();
emp.canFighting();
}
}
合成/聚合复用原则
合成/聚合复用原则就是说尽量使用聚合的方式而不要使用继承的方式达到复用原有代码目的。
案例:
尽量使用聚合的方式而不要使用继承的方式达到目的。代码的耦合度关系:泛化= 实现> 组合> 聚合> 关联> 依赖
java
/**
* 聚合复用原则
*/
public class SyntheticMultiplexingDemo01 {
public static void main(String[] args) {
TestB testB = new TestB();
testB.testMethod1();
TestC testC = new TestC();
testC.testMethod1();
TestD testD = new TestD();
//使用聚合的时候要注意避免空指针异常
testD.setTestA(new TestA());
testD.testMethod1();
TestE testE = new TestE();
testE.testMethod1(new TestA());
}
}
class TestA {
public void testMethod1() {
System.out.println("执行了TestA的testMethod1方法......");
}
}
//使用继承的方式去使用TestA的testMethod1方法。
class TestB extends TestA {
public void testMethod1() {
System.out.println("TestB:");
super.testMethod1();
}
}
//使用组合的方式去用使用TestA的testMethod1方法。
class TestC {
private TestA testA = new TestA();
public void testMethod1() {
System.out.println("TestC:");
testA.testMethod1();
}
}
//使用聚合的方式去用使用TestA的testMethod1方法。
class TestD {
private TestA testA;
public void setTestA(TestA testA) {
this.testA = testA;
}
public void testMethod1() {
System.out.println("TestD:");
testA.testMethod1();
}
}
//使用依赖的方式去用使用TestA的testMethod1方法。
class TestE {
public void testMethod1(TestA testA) {
System.out.println("TestE:");
testA.testMethod1();
}
}
迪米特法原则/最少知道原则
迪米特法原则也叫最少知道原则,就是说一个类对自己依赖的类知道得越少越好,对于依赖的类而言,类不管多么复杂也尽量将逻辑封装在其内部,对外只提供public的方法,但是不能对外直接提供访问属性的权限。通俗来说就是只使用直接联系的类,不要使用出现没有直接联系的陌生的类,直接联系的类包括:是类的成员变量、是类中方法的返回值、是类中方法的参数最直接的说法就是不要让一个既不是成员变量类型也不是类中方法的返回值类型还不是类中方法的参数类型的类去成为在局部变量的类型。
反例:
以打印中学学生信息为例说明迪米特法则,在打印学生信息的方法中,中学管理类中出现了跟自己当前类没有直接关联的初中学生类,既不是类中的成员变量,类中方法的返回值,而直接成为了一个局部变量。
java
/**
* 迪米特法原则-反例
*/
public class DimifatPrincipleDemo01 {
public static void main(String[] args) {
StudentManager studentManager = new StudentManager();
studentManager.printAllStudent(new JuniorMiddleStudentManager());
}
}
/**
* 高中部学生类
*/
class Student {
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
/**
* 初中部学生类
*/
class JuniorMiddleStudent {
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
/**
* 中学学生管理类
*/
class StudentManager {
/**
* 获取所有高中学生
*/
public List<Student> getAllStudent() {
List<Student> studentList = new ArrayList<>();
for (int i = 1; i <= 5; i++) {
Student hightStudent = new Student();
hightStudent.setId("高中学生:" + i + "号");
studentList.add(hightStudent);
}
return studentList;
}
/**
* 打印所有中学学生信息
*/
public void printAllStudent(JuniorMiddleStudentManager middleStudentManager) {
List<JuniorMiddleStudent> juniorMiddleStudentList = middleStudentManager.getAllStudent();
System.out.println("初中学生:=======================");
for (JuniorMiddleStudent middleStudent : juniorMiddleStudentList) {
System.out.println(middleStudent.getId());
}
List<Student> allHightStudent = getAllStudent();
System.out.println("高中学生:=======================");
for (Student hightStudent : allHightStudent) {
System.out.println(hightStudent.getId());
}
}
}
/**
* 初中学生管理类
*/
class JuniorMiddleStudentManager {
/**
* 获取所有初中学生
*/
public List<JuniorMiddleStudent> getAllStudent() {
List<JuniorMiddleStudent> middleStudentList = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
JuniorMiddleStudent middleStudent = new JuniorMiddleStudent();
middleStudent.setId("初中学生:" + i + "号");
middleStudentList.add(middleStudent);
}
return middleStudentList;
}
}
正例:
改进思路:不要让一个不是直接联系的类只以局部变量的形式出现,如果是类中方法的参数最直接的做法就是只引用这个参数的值或方法即可。
java
/**
* 迪米特法原则-正例
*/
public class DimifatPrincipleDemo02 {
public static void main(String[] args) {
StudentManager1 studentManager = new StudentManager1();
studentManager.printAllStudent(new JuniorMiddleStudentManager1());
}
}
/**
* 高中学生类
*/
class Student1 {
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
/**
* 初中部学生类
*/
class JuniorMiddleStudent1 {
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
/**
* 中学学生管理类
*/
class StudentManager1 {
/**
* 获取所有高中学生
*/
public List<Student1> getAllStudent() {
List<Student1> studentList = new ArrayList<>();
for (int i = 1; i <= 5; i++) {
Student1 hightStudent = new Student1();
hightStudent.setId("高中学生:" + i + "号");
studentList.add(hightStudent);
}
return studentList;
}
/**
* 打印所有中学学生信息
*/
public void printAllStudent(JuniorMiddleStudentManager1 middleStudentManager) {
middleStudentManager.printAllMiddleStudent();
List<Student1> allHightStudent = getAllStudent();
System.out.println("高中学生:=======================");
for (Student1 hightStudent : allHightStudent) {
System.out.println(hightStudent.getId());
}
}
}
/**
* 初中学生管理类
*/
class JuniorMiddleStudentManager1 {
/**
* 获取所有初中学生
*/
public List<JuniorMiddleStudent1> getAllStudent() {
List<JuniorMiddleStudent1> middleStudentList = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
JuniorMiddleStudent1 middleStudent = new JuniorMiddleStudent1();
middleStudent.setId("初中学生:" + i + "号");
middleStudentList.add(middleStudent);
}
return middleStudentList;
}
/**
* 打印所有初中学生信息
*/
public void printAllMiddleStudent() {
List<JuniorMiddleStudent1> juniorMiddleStudentList = getAllStudent();
System.out.println("初中学生:=======================");
for (JuniorMiddleStudent1 middleStudent : juniorMiddleStudentList) {
System.out.println(middleStudent.getId());
}
}
}
单一职能原则和接口隔离原则的区别和注意事项
-
接口隔离原则的好处: 避免接口污染、提高灵活性、提供定制服务、实现高内聚。
-
接口隔离原则和单一职能原则非常类似。单一职能原则要求类或者接口的职能是单一的,而接口隔离原则要求接口尽量细化,它们有异曲同工之妙,都是要让我们的接口功能尽量单一,尽量小。但是,单一职能原则的着重点是在"职能",而接口隔离原则只单纯地要求接口最小化。
-
那么,如果已经满足单一职能原则的接口,在当前的需求下还可以继续细化,那么还需要细化吗?答案是不要再细化了。在实践中,接口设计的粒度越小,系统就越灵活,这是事实。但是灵活的同时也带来了系统的复杂化,导致开发难度增加。
-
所以接口并不是越小越好,必须要有一个度。当单一职能原则和接口隔离原则存在矛盾时,以满足单一职能原则为底线,也就是说接口隔离原则不是在软件开发设计中要绝对遵守的原则。
-
例如:在Service中调用Dao是违反了接口隔离原则,但是是在满足单一职能原则的前提下违反的,所以是合理的,还有我们经常使用的List的实现类ArrayList也是这个道理。