设计模式的目的
- 重用性:相同功能的代码,不用多次编写。
- 可读性:编程规范性,便于其他程序员的阅读和理解。
- 可扩展性:当需要增加新的功能时,非常的方便。
- 可靠性:当增加新的功能后,对原来的功能没有影响。
- 使程序呈现高内聚,低耦合的特性。
单一职责原则
基本介绍
- 对类来说,级一个类应该只负责一项职责。如类A负责两个不同职责:职责1,职责2。当职责1需求变更而改变A时,可能造成职责2执行错误,所以需要将类A的粒度分解为A1,A2。
案例
public class SingleResponsibility01 {
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 SingleResponsibility02 {
public static void main(String[] args) {
Vehicle vehicle = new Vehicle();
vehicle.runRoad("汽车");
vehicle.runAir("飞机");
vehicle.runWater("轮船");
}
}
class Vehicle{
public void runRoad(String vehicle){
System.out.println(vehicle + "在公路上跑......");
}
public void runAir(String vehicle){
System.out.println(vehicle + "在天空上飞......");
}
public void runWater(String vehicle){
System.out.println(vehicle + "在水中游......");
}
}
注意事项和细节
- 降低类的复杂性,一个类只负责一项职责。
- 提高类的可读性,可维护性。
- 降低变更引起的风险。
- 通常情况下,我们应当遵守单一职责原则,只有逻辑足够简单,才可以在代码级违反单一职责原则;只有类中方法数量足够少,可以在方法级别保持单一职责原则。
接口隔离原则
基本介绍
- 客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上。
案例
- 不遵循解扣隔离原则
interface Interface1{
void operation1();
void operation2();
void operation3();
void operation4();
void operation5();
}
class B implements Interface1{
@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");
}
@Override
public void operation5() {
System.out.println("B 实现了 operation5");
}
}
class D implements Interface1{
@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");
}
@Override
public void operation5() {
System.out.println("D 实现了 operation5");
}
}
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{
public void depend1(Interface1 i){
i.operation1();
}
public void depend4(Interface1 i){
i.operation4();
}
public void depend5(Interface1 i){
i.operation5();
}
}
- 遵循接口隔离原则
interface Interface1{
void operation1();
}
interface Interface2{
void operation2();
void operation3();
}
interface Interface3{
void operation4();
void operation5();
}
class B implements Interface1, Interface2{
@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");
}
}
class D implements Interface1, Interface3{
@Override
public void operation1() {
System.out.println("D 实现了 operation1");
}
@Override
public void operation4() {
System.out.println("D 实现了 operation4");
}
@Override
public void operation5() {
System.out.println("D 实现了 operation5");
}
}
class A{
public void depend1(Interface1 i){
i.operation1();
}
public void depend2(Interface2 i){
i.operation2();
}
public void depend3(Interface2 i){
i.operation3();
}
}
class C{
public void depend1(Interface1 i){
i.operation1();
}
public void depend4(Interface3 i){
i.operation4();
}
public void depend5(Interface3 i){
i.operation5();
}
}
依赖倒转原则
基本介绍
- 高层模块不应该依赖底层模块,二者都应该依赖其抽象。
- 抽象不应该依赖细节,细节应该依赖抽象。
- 依赖倒转的中心思想是面向接口编程。
- 依赖倒转原则是基于这样的设计理念:相对于细节的多边形,抽象的东西要稳定的多。以抽象为基础搭建的架构比以细节为基础的架构要稳定的多。在java中,抽象指的是接口或者抽象类,细节就是具体的实现类。
- 使用解扣或者抽象类的目的是制定好规范,而不涉及任何具体操作,把展现细节的任务交给他们的实现类去做。
- 依赖关系传递的三种方式:接口传递、构造方法传递、setter方式传递。
案例
public class DependencyInversion01 {
public static void main(String[] args) {
Person person = new Person();
person.receive(new Email());
}
}
class Email{
public String getInfo(){
return "邮件:hello, world!";
}
}
class Person{
public void receive(Email email){
System.out.println(email.getInfo());
}
}
public class DependencyInversion02 {
public static void main(String[] args) {
Person person = new Person();
person.receive(new Email());
person.receive(new WeiXin());
}
}
interface IReceive{
String getInfo();
}
class Email implements IReceive{
@Override
public String getInfo() {
return "邮件:hello world!";
}
}
class WeiXin implements IReceive{
@Override
public String getInfo() {
return "微信:hello world!";
}
}
class Person{
public void receive(IReceive receive){
System.out.println(receive.getInfo());
}
}
注意事项和细节
- 底层模块尽量都要有抽象类或者接口,或者两者都有,程序稳定性更好。
- 变量的声明类型尽量是抽象类或者接口,这样我们的变量引用和实际对象间,就存在一个缓冲层,利于程序扩展和优化。
- 继承时遵循里氏替换原则。
里氏替换原则
基本介绍
- 如果对每个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有对象o1都代换为o2时,程序P的行为没有发生变化,那么类型T2是类型T1的子类型。换句话说,所有引用基类的地方必须能透明地使用其子类的对象。
- 在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法。
- 里氏替换原则告诉我们,继承实际上让两个类的耦合性增强了,在适当的情况下,可以通过聚合,组合,依赖来解决问题。
案例
public class Liskov {
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));
}
}
class A{
public int func1(int num1, int num2){
return num1 - num2;
}
}
class B extends A{
@Override
public int func1(int a, int b){
return a + b;
}
public int func2(int a, int b){
return func1(a, b) + 9;
}
}
public class Liskov {
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));
System.out.println("11-3=" + b.func3(11, 3));
}
}
class Base{
}
class A extends Base{
public int func1(int num1, int num2){
return num1 - num2;
}
}
class B extends Base{
private A a = new A();
public int func1(int a, int b){
return a + b;
}
public int func2(int a, int b){
return func1(a, b) + 9;
}
public int func3(int a, int b){
return this.a.func1(a, b);
}
}
开闭原则
基本介绍
- 开闭原则是编程中最基础、最重要的设计原则。
- 一个软件实体如类,模块和函数应该对扩展开放,对修改关闭。用抽象构架框架,用实现扩展细节。
- 当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
- 编程中遵循其他原则,以及使用设计模式的目的就是遵循开闭原则。
案例
public class Ocp {
public static void main(String[] args) {
GraphicEditor graphicEditor = new GraphicEditor();
graphicEditor.drawShape(new Circle());
graphicEditor.drawShape(new Rectangle());
}
}
class GraphicEditor{
public void drawShape(Shape shape){
if (shape.type == 1){
drawRectangle();
} else if (shape.type == 2){
drawCircle();
}
}
public void drawRectangle(){
System.out.println("矩形");
}
public void drawCircle(){
System.out.println("圆形");
}
}
class Shape{
int type;
}
class Rectangle extends Shape{
Rectangle(){
super.type = 1;
}
}
class Circle extends Shape{
Circle(){
super.type = 2;
}
}
public class Ocp {
public static void main(String[] args) {
GraphicEditor graphicEditor = new GraphicEditor();
graphicEditor.drawShape(new Circle());
graphicEditor.drawShape(new Rectangle());
}
}
class GraphicEditor{
public void drawShape(Shape shape){
shape.draw();
}
}
interface Shape{
void draw();
}
class Rectangle implements Shape{
@Override
public void draw() {
System.out.println("矩形");
}
}
class Circle implements Shape{
@Override
public void draw() {
System.out.println("圆形");
}
}
迪米特法则
基本介绍
- 一个对象应该对其他对象保持最少的了解。
- 类与类关系越密切,耦合度最大。
- 迪米特法则,又称最少知道原则,即一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供public方法,不对外泄露任何信息。
- 迪米特法则还有个更简单的定义:只与直接的朋友通信。
- 直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友。耦合的方式很多,依赖,关联,组合,聚合等。其中,我们称出现成员变量,方法参数,方法返回值中的类为直接的朋友,而出现在局部变量中的类不是直接的朋友。也就是说,陌生的类最好不要以局部变量的形式出现在类的内部。
案例
public class Demeter {
public static void main(String[] args) {
SchoolManage schoolManage = new SchoolManage();
schoolManage.printAllEmployee(new CollegeManager());
}
}
class Employee{
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
class CollegeEmployee{
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
class CollegeManager{
public List<CollegeEmployee> getAllEmployee(){
List<CollegeEmployee> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
CollegeEmployee collegeEmployee = new CollegeEmployee();
collegeEmployee.setId("学院员工id="+i);
list.add(collegeEmployee);
}
return list;
}
}
class SchoolManage{
public List<Employee> getAllEmployee(){
List<Employee> list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
Employee employee = new Employee();
employee.setId("学校总部员工id="+i);
list.add(employee);
}
return list;
}
void printAllEmployee(CollegeManager manager){
List<CollegeEmployee> employeeList = manager.getAllEmployee();
for (CollegeEmployee collegeEmployee : employeeList) {
System.out.println(collegeEmployee.getId());
}
System.out.println("======================");
List<Employee> employeeList1 = this.getAllEmployee();
for (Employee employee : employeeList1) {
System.out.println(employee.getId());
}
}
}
public class Demeter {
public static void main(String[] args) {
SchoolManage schoolManage = new SchoolManage();
schoolManage.printAllEmployee(new CollegeManager());
}
}
class Employee{
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
class CollegeEmployee{
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
class CollegeManager{
public List<CollegeEmployee> getAllEmployee(){
List<CollegeEmployee> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
CollegeEmployee collegeEmployee = new CollegeEmployee();
collegeEmployee.setId("学院员工id="+i);
list.add(collegeEmployee);
}
return list;
}
public void printEmployee(){
List<CollegeEmployee> employeeList = this.getAllEmployee();
for (CollegeEmployee collegeEmployee : employeeList) {
System.out.println(collegeEmployee.getId());
}
}
}
class SchoolManage{
public List<Employee> getAllEmployee(){
List<Employee> list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
Employee employee = new Employee();
employee.setId("学校总部员工id="+i);
list.add(employee);
}
return list;
}
void printAllEmployee(CollegeManager manager){
manager.printEmployee();
System.out.println("======================");
List<Employee> employeeList1 = this.getAllEmployee();
for (Employee employee : employeeList1) {
System.out.println(employee.getId());
}
}
}
注意事项和细节
- 迪米特法则和核心是降低类之间的耦合。
- 注意:由于每个类都减少了不必要的依赖,因此迪米特法则只是要求降低类间耦合关系,并不是要求完全没有依赖关系。
合成复用原则
基本介绍
设计原则核心思想
- 找出应用中可能需要变化之处,把他们独立出来,不要和那些不需要变化的代码混在一起。
- 针对接口编程,而不是针对实现编程。
- 为了交互对象之间的松耦合而努力设计。