章节目录:
一、设计模式介绍
设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。
该概念是1990年从建筑设计领域引入到计算机科学。
设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。
设计模式的本质是:软件的维护性,通用性,并降低软件的复杂度。
出现的节点:
面向对象特性
->功能模块
(设计模式+算法(数据结构)) ->框架
(使用到多种设计模式) ->架构
(服务器集群)。
二、设计模式类型
- 设计模式分为三种类型,共23种:
三、七大原则
设计模式原则:其实就是程序员在编程时,应当遵守的原则,也是各种设计模式的基础(即:设计模式为什么这样设计的依据。)
原则一:单一职责原则
“ 对类来说,即一个类应该只负责一项职责。”
- 代码示例:
// ----------------实现方式1:----------------
/* *
* 有一个交通工具类。
* 它有自己的run()方法,可以使交通工具在[公路上运行]。
*/
static class Vehicle {
public void run(String vehicle) {
System.out.println(vehicle + " 在公路上运行");
}
}
public static void main(String[] args) {
Vehicle vehicle = new Vehicle();
vehicle.run("公交车");
// 公交车 在公路上运行
vehicle.run("飞机");
// 飞机 在公路上运行
// 总结:当交通工具为[飞机]的时候,仍然是在[公路上运行]?
// *很显然这是不合理的。
}
// ----------------实现方式2:----------------
/* *
* 针对不同的工具,有不同类。
* 都有自己特有的run()方法。
*/
static class RoadVehicle {
public void run(String vehicle) {
System.out.println(vehicle + " 在公路上运行");
}
}
static class SkyVehicle {
public void run(String vehicle) {
System.out.println(vehicle + " 在天空飞行");
}
}
static class WaterVehicle {
public void run(String vehicle) {
System.out.println(vehicle + " 在水中运行");
}
}
public static void main(String[] args) {
RoadVehicle roadVehicle = new RoadVehicle();
roadVehicle.run("公交车");
// 公交车 在公路上运行
SkyVehicle skyVehicle = new SkyVehicle();
skyVehicle.run("飞机");
// 飞机 在天空飞行
WaterVehicle waterVehicle = new WaterVehicle();
waterVehicle.run("轮船");
// 轮船 在水中运行
// 总结:虽然在[类层面]保持了单一职责的原则,但是需要创建三个不同对象,浪费了一定资源。
}
// ----------------实现方式3:----------------
/* *
* 有一个交通工具类。
* 它针对不同的交通工具,有不同的run()方法。
*/
static class Vehicle {
public void runRoad(String vehicle) {
System.out.println(vehicle + " 在公路上运行");
}
public void runSky(String vehicle) {
System.out.println(vehicle + " 在天空飞行");
}
public void runWater(String vehicle) {
System.out.println(vehicle + " 在水中运行");
}
}
public static void main(String[] args) {
Vehicle vehicle = new Vehicle();
vehicle.runRoad("公交车");
// 公交车 在公路上运行
vehicle.runSky("飞机");
// 飞机 在天空飞行
vehicle.runWater("轮船");
// 轮船 在水中运行
// 总结:在[方法层],实现单一职责的原则。
// 类方法更加灵活,且易于维护。
}
- 注意事项和细节:
降低类的复杂度,
一个类只负责一项职责
。提高类的
可读性
,可维护性
。降低变更引起的风险。
通常情况下,我们应当遵守单一职责原则,只有逻辑足够简单,才可以在代码级违反单一职责原则;只有类中方法数量足够少,则可以在方法级别保持单一职责原则。
原则二:接口隔离原则
“ 客户端不应该依赖它不需要的接口,即一个类对另一个类的
依赖
应该建立在最小的接口
上。”
只实现需要的
接口方法。
- 未遵循隔离原则:
- 遵循隔离原则:
原则三:依赖倒转原则
高层模块不应该依赖低层模块,二者都应该
依赖其抽象
。抽象不应该依赖细节,细节应该依赖抽象。
依赖倒转的中心思想是
面向接口编程
。依赖倒转原则是基于这样的设计理念:相对于细节的多变性,
抽象的东西要稳定的多
。以抽象为基础搭建的架构比以细节为基础的架构要稳定的多。在Java中,抽象指的是接口或抽象类,细节就是具体的实现类。使用
接口
或抽象类
的目的就是制定好规范
,而不涉及任何具体的操作,把展现细节
的任务交给他们的实现类
去完成。
- 未使用依赖倒转原则:
/* *
* Person类有一个接收消息的方法,该方法需要传入Email对象进行调用。
*/
static class Person {
public void receive(Email email) {
System.out.println(email.getInfo());
}
}
static class Email {
public String getInfo() {
return "email info:hello,world!";
}
}
public static void main(String[] args) {
Person person = new Person();
person.receive(new Email());
// email info:hello,world!
// 总结:当获取的对象为新的类型,例如:微信,短信等。
// 则Person类需要增加新的接收方法进行实现。
// 显然这是不利于拓展的。
}
- 使用依赖倒转原则:
/* *
* 采用依赖倒转原则,增加IReceiver接口,有公共的getInfo()方法。
* 让Email、Wechat类去进行具体实现。
*/
interface IReceiver {
String getInfo();
}
static class Email implements IReceiver {
@Override
public String getInfo() {
return "email info:hello,world!";
}
}
static class Wechat implements IReceiver {
@Override
public String getInfo() {
return "wechat info:hello,world!";
}
}
static class Person {
/* *
* 传入IReceiver接口对象
*/
public void receive(IReceiver receiver) {
System.out.println(receiver.getInfo());
}
}
public static void main(String[] args) {
Person person = new Person();
person.receive(new Email());
// email info:hello,world!
person.receive(new Wechat());
// wechat info:hello,world!
// 总结:这里是对抽象接口进行依赖,而不再是具体的实现类。
// IReceiver接口相当于充当了一个缓冲层,后期需要拓展新的类只需要去实现该接口方法即可。
}
-
依赖传递的三种方式:
-
公共实现部分
/**
* 公共的电视接口
*/
interface ITV {
void play();
}
/**
* 公共的电视实现类
*/
static class ChangHong implements ITV {
@Override
public void play() {
System.out.println("长虹电视机,打开");
}
}
- 方式1:接口传递
/* *
* 开关接口
*/
interface IOpenAndClose {
void open(ITV tv);
}
/**
* 方式1:通过接口传递依赖
*/
static class OpenAndClose implements IOpenAndClose {
@Override
public void open(ITV tv) {
// 调用
tv.play();
}
}
public static void main(String[] args) {
ChangHong changHong = new ChangHong();
OpenAndClose openAndClose = new OpenAndClose();
// 传入changHong实现类,完成调用。
openAndClose.open(changHong);
// 长虹电视机,打开
}
- 方式2:构造方法传递
interface IOpenAndClose {
void open();
}
/**
* 方式2:通过构造方法依赖传递
*/
static class OpenAndClose implements IOpenAndClose {
// 成员
public ITV tv;
// 通过传入的对象赋值给成员,进行构造
public OpenAndClose(ITV tv) {
this.tv = tv;
}
@Override
public void open() {
// 调用
this.tv.play();
}
}
public static void main(String[] args) {
ChangHong changHong = new ChangHong();
// 传入具体的changHong对象完成构造
OpenAndClose openAndClose = new OpenAndClose(changHong);
openAndClose.open();
// 长虹电视机,打开
}
- 方式3:setter方式传递
interface IOpenAndClose {
void open(); // 抽象方法
void setTv(ITV tv);
}
/* *
* 方式3:通过setter方法传递
*/
static class OpenAndClose implements IOpenAndClose {
private ITV tv;
@Override
public void setTv(ITV tv) {
this.tv = tv;
}
@Override
public void open() {
this.tv.play();
}
}
public static void main(String[] args) {
ChangHong changHong = new ChangHong();
OpenAndClose openAndClose = new OpenAndClose();
// 设置具体对象
openAndClose.setTv(changHong);
openAndClose.open();
// 长虹电视机,打开
}
- 注意事项和细节:
低层模块尽量都要有
抽象类
或接口
,或者两者都有,程序稳定性更好。变量的声明类型尽量是抽象类或接口,这样我们的变量引用和实际对象间,就存在一个
缓冲层
,利于程序拓展和优化。继承时遵循
里氏替换原则
。
原则四:里氏替换原则
在使用继承时,遵循里氏替换原则,在
子类中尽量不要重写父类方法
。里氏替换原则告诉我们:
继承
实际上让两个类耦合性
增强了,在适当的情况下,可以通过聚合
,组合
,依赖
来解决问题。通常的做法是:原来的父类和子类都继承一个更通俗的基类,原有的继承关系去掉,采用依赖,聚合,组合等关系代替。
原则五:开闭原则 - OCP
开闭原则:是编程中最基础、最重要的设计原则。
一个软件实体如
类
,模块和函数应该对拓展开放
(对提供方),对修改关闭
(对使用方)。用抽象构建框架,用实现拓展细节。当软件需要变化时,尽量通过拓展软件实体的行为来实现变化,而不是通过修改已有的代码来实现。
编程中遵循其它原则,以及使用设计模式的目的就是遵循开闭原则。
原则六:迪米特法则
迪米特法则又叫最
少知道原则
:即一个对象应该对其他对象保持最少的了解
。类与类关系越密切,耦合度越大。
迪米特法则简单定义:
只与直接的朋友通信。
直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,那么两个对象之间就是朋友关系 。(耦合方式:依赖,关联,组合,聚合等。)其中,出现
成员变量
,方法参数
,方法返回值
中的类为直接的朋友,而出现在局部变量中的类不是直接朋友。(陌生的类最好不要以局部变量的形式出现在类的内部。)
原则七:合成复用原则
“ 尽量使用合成/聚合的方式,而不是继承。”
找出应用中可能需要
变化之处
,把它们独立出来
,不要和那些不需要变化的代码混在一起。针对
接口
编程,而不是针对实现
编程。
四、结束语
“-------怕什么真理无穷,进一寸有一寸的欢喜。”
微信公众号搜索:饺子泡牛奶。