目录
前言
合理的使用设计模式可以简化开发,提高代码的阅读量,让代码看起来更加的精简,代码结构层次更加的清晰,学习设计模式可以让我们的思维更加的灵活和抽象化。
代码中从不使用设计模式的缺点是:代码看起来繁杂、凌乱、臃肿和代码占用大小增加,阅读性差,找不到阅读入口等等。
但是如果不熟悉设计模式,没有深入的研究过,不了解它的核心思想,生搬硬套的使用设计模式,也会出现线程安全,资源泄露和代码性能降低等等问题。
这篇文章就是以我的理解对常用的设计模式进行讲解,帮助读者快速的了解这些设计模式,但是怎么使用它,还是要看具体的场景。
一、设计原则
简单的介绍一下六大设计原则,因为设计模式离不开这些原则。
1.1 开闭原则
对外扩展开发,对内修改关闭。
如:一个人单身男每天必须要吃早餐,而且就两样鸡蛋和牛奶,其他啥也不吃。
public class Person {
public void eatBreakFast(){
System.out.println("吃鸡蛋");
System.out.println("喝牛奶");
}
}
假如有一天他找到女朋友了,女朋友说:你必须要吃面包。(女朋友的话必须听,不能反驳)
这个时候在不满足开闭原则的话,是下面的样子。
public class Person {
public void eatBreakFast(){
System.out.println("吃鸡蛋");
System.out.println("喝牛奶");
System.out.println("吃面包");
}
}
那么满足开闭原则的话是这样的。
public class Person {
public void eatBreakFast(){
System.out.println("吃鸡蛋");
System.out.println("喝牛奶");
}
public void eatBreakFastOfHasGirlFriend(){
System.out.println("吃鸡蛋");
System.out.println("喝牛奶");
System.out.println("吃面包");
}
}
这样一来,我们不改变之前的逻辑和功能,只对它进行扩展。调用者只需要调用新的方法就行了。 这样有一天调用者想调用之前的方法,只需要换个方法名就行了。(如果那一天他失恋了,没有女朋友了,他还可以按照之前的方式吃早餐。。。。。他应该很开心吧。。。。。。)
1.2 单一职责原则
一个类、接口或者方法只允许有一个职责。
用方法来说明,假设一个方法就是一个人。
如果不满足单一职责原则,是这样的。
public class Person {
public void person(){
System.out.println("我是一名电工,会修电,我有相关证书");
System.out.println("我是一名厨师,会做饭,我有相关证书");
System.out.println("我是一名教师,会教书,我有相关证书");
System.out.println("我是一名医生,会看病,我有相关证书");
}
}
我们为了不让这个方法(人)臃肿(累死),所以单一职责原则出现了。
public class Person {
public void person1(){
System.out.println("我是一名电工,会修电,我有相关证书");
}
public void person2(){
System.out.println("我是一名厨师,会做饭,我有相关证书");
}
public void person3(){
System.out.println("我是一名教师,会教书,我有相关证书");
}
public void person4(){
System.out.println("我是一名医生,会看病,我有相关证书");
}
}
单一职责的原理就是,为了在有一天要变更某一个功能时,变动的地方更小,只涉及某一个功能。(假设这个人很厉害,辛辛苦苦到80岁,终于所有证书都有了,都可以合法做这些事挣钱养家了,某一天接到通知,所有的证书要重新考试,问:此时他的心理阴影面。。。。。。。。)
1.3 依赖倒置原则
依赖抽象而不是依赖实现。抽象不应该依赖细节,细节应该依赖抽象。高层模块不能依赖低层模块,二者都应该依赖抽象。
假如不满足依赖倒置原则,喜羊羊吃草是这样的。
// 小草
public interface Grass {
void grassType();
}
// 红颜色的小草
public class RedGrass implements Grass{
@Override
public void grassType() {
System.out.println("我是红颜色的小草,来吃我吧");
}
}
// 绿颜色的小草
public class GreenGrass implements Grass{
@Override
public void grassType() {
System.out.println("我是绿颜色的小草,来吃我吧");
}
}
// 测试代码
public class Test {
// 红色的草
static RedGrass redGrass;
public Test(RedGrass redGrass) {
this.redGrass = redGrass;
}
public static void main(String[] args) {
new Test(new RedGrass());
System.out.println("我是喜羊羊,我要吃草");
redGrass.grassType();
}
}
假如有一天,这个世界上没有红色的草,那么喜羊羊就没了。。。。。,满足依赖倒置原则时,是这样的。
public class Test {
// 是草就行
static Grass grass;
public Test(Grass grass) {
this.grass = grass;
}
public static void main(String[] args) {
new Test(new RedGrass());
System.out.println("我是喜羊羊,我要吃草");
grass.grassType();
new Test(new GreenGrass());
System.out.println("我是喜羊羊,我要吃草");
grass.grassType();
}
}
这样一来,不知道你们有没有明白依赖倒置原则的思想,反正喜羊羊是不会饿死的,是个草它就可以吃。。。。。。。。
1.4 接口分离原则
采用多个与特定客户类有关的接口比采用一个通用的接口要好。
假如一个有女朋友的人,他女朋友一星期,每一天要穿不同的衣服,这些衣服都要他提前准备好。不满足接口分离原则的时候是这样。
public class Boy implements Clothes{
public void getClothes(String date){
if (date.equals("星期一")){
System.out.println("今天你穿:牛仔裤搭配高跟鞋");
} else if (date.equals("星期二")){
System.out.println("今天你穿:牛仔裤搭配运动鞋");
}else if (date.equals("星期三")){
System.out.println("今天你穿:牛仔裤搭配帆布鞋");
}else if (date.equals("星期四")){
System.out.println("今天你穿:裙子搭配高跟鞋");
}else if (date.equals("星期五")){
System.out.println("今天你穿:裙子搭配运动鞋");
}else if (date.equals("星期六")){
System.out.println("今天你穿:裙子搭配帆布鞋");
}else if (date.equals("星期七")){
System.out.println("今天你穿:九分裤搭配运动鞋");
}
}
}
使用接口分离原则是这样的。
public class Boy implements Clothes{
@Override
public void getMondayClothes(String date) {
System.out.println("今天你穿:牛仔裤搭配高跟鞋");
}
@Override
public void getTuesdayClothes(String date) {
System.out.println("今天你穿:牛仔裤搭配运动鞋");
}
@Override
public void getWednesdayClothes(String date) {
System.out.println("今天你穿:牛仔裤搭配帆布鞋");
}
@Override
public void getThuresdayClothes(String date) {
System.out.println("今天你穿:裙子搭配高跟鞋");
}
@Override
public void getFridayClothes(String date) {
System.out.println("今天你穿:裙子搭配运动鞋");
}
@Override
public void getSaturdayClothes(String date) {
System.out.println("今天你穿:裙子搭配帆布鞋");
}
@Override
public void getSundayClothes(String date) {
System.out.println("今天你穿:九分裤搭配运动鞋");
}
}
1.5 迪米特法则
一个类对于其他类知道的越少越好。一个对象应当对其他对象有尽可能少的了解,只和朋友通信,不和陌生人说话。
public class Wolf {
public static String name = "灰太狼";
}
public class Test {
private String name = "喜羊羊";
public static void main(String[] args) {
System.out.println("我是" + name + ",小灰灰的爸爸是:" + Wolf.name);
}
}
不满足迪米特法则,那么喜羊羊就可以知道小灰灰的爸爸是谁,灰太狼不想让它的名字被别的羊知道,就要用满足迪米特法则。
public class Wolf {
private static String name = "灰太狼";
}
public class Test {
private String name = "喜羊羊";
public static void main(String[] args) {
//得不到Wolf.name
System.out.println("我是" + name + ",小灰灰的爸爸是:" + Wolf.name);
}
}
1.6 里氏替换原则
派生类(子类)对象可以在程式中代替其基类(超类)对象,但是不能重写父类的非抽象方法。
这个就是多态的使用,不用讲解了。
二、设计模式
2.1 单例模式
属于创建型模式,它提供了一种创建对象的最佳方式。满足以下三点:
单例类只能有一个实例。
单例类必须自己创建自己的唯一实例。
单例类必须给所有其他对象提供这一实例。
2.1.1 饿汉式
public class Singleton {
private Singleton() {}
private static Singleton singleton= new Singleton();
public static Singleton getSingleton(){
return singleton;
}
}
2.1.2 懒汉式非安全
public class Singleton {
private static Singleton singleton;
private Singleton (){}
public static Singleton getInstance() {
// 多线程并发到这时,不能保证单例,所以不安全
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
2.1.3 懒汉式安全
public class Singleton {
private static Singleton singleton;
private Singleton (){}
// 安全是安全了,可以高并发都在这阻塞的话,降低性能
public static synchronized Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
2.1.4 double+check
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
// 为空时才抢锁创建
if (singleton == null) {
// 加锁
synchronized (Singleton.class) {
// 再次判断,如果抢到锁发现已经不为空,证明之前抢到锁的已经创建了
if (singleton == null) {
// 没有的话就创建一个
singleton = new Singleton();
}
}
}
// 直接返回
return singleton;
}
}
2.1.5 静态内部类
public class Singleton {
private static class SingletonHolder {
private static final Singleton SINGLETON = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.SINGLETON;
}
}
2.1.6 枚举
public enum Singleton {
SINGLETON;
public void doooooo() {
//.........
}
}
public class Test {
public static void main(String[] args) {
Singleton.SINGLETON.doooooo();
}
}
根据使用什么类型的单例,还是要结合业务和实际的需要。
2.2 建造者模式
使用多个简单的对象一步一步构建成一个复杂的对象。属于创建型模式
//阳台
public class Balcony {
}
// 客厅
public class DrawingRoom {
}
// 书房
public class Study {
}
// 卫生间
public class Toilet {
}
// 构建一个房子,按照不同人的爱好
public class Room {
// 阳台
private Balcony balcony;
// 客厅
private DrawingRoom drawingRoom;
// 书房
private Study study;
// 卫生间
private Toilet Toilet;
public Room(RoomFactory.Builder builder) {
this.balcony = builder.balcony;
this.drawingRoom = builder.drawingRoom;
this.study = builder.study;
this.Toilet = builder.Toilet;
}
}
public class RoomFactory {
private RoomFactory() {}
// 内部的工具类
public static class Builder{
// 阳台
public Balcony balcony;
// 客厅
public DrawingRoom drawingRoom;
// 书房
public Study study;
// 卫生间
public Toilet Toilet;
public Builder builderBalcony(Balcony balcony) {
this.balcony = balcony;
return this;
}
public Builder builderDrawingRoom(DrawingRoom drawingRoom) {
this.drawingRoom = drawingRoom;
return this;
}
public Builder builderStudy(Study study) {
this.study = study;
return this;
}
public Builder builderToilet(builder.Toilet toilet) {
Toilet = toilet;
return this;
}
// 构建房子
public Room build(){
return new Room(this);
}
private Builder() {}
}
// 对外入口
public static RoomFactory.Builder builder(){
return new Builder();
}
}
构建两个不同设计的房子
public class Test {
public static void main(String[] args) {
Balcony balcony = new Balcony();
DrawingRoom drawingRoom = new DrawingRoom();
Study study = new Study();
Toilet toilet = new Toilet();
Room room1= RoomFactory.builder().builderBalcony(balcony).builderStudy(study).build();
Room room2= RoomFactory.builder().builderToilet(toilet).builderDrawingRoom(drawingRoom).build();
}
}
2.3 适配器模式
两个不兼容的接口之间的桥梁。属于结构型模式
假设现在的播放器只支持MP4格式,不支持其他格式。
public interface Video {
void play(String format);
}
public class CommonVideo implements Video{
@Override
public void play(String format) {
if (format.equals("MP4")){
System.out.println("播放MP4格式视频");
}else {
System.out.println("不支持的播放格式");
}
}
}
如果我们要进行升级,让它支持更多的格式,可以使用适配器模式。
// 定义高级视频播放器
public interface AdvancedVideo {
void playAVI();
void playWMV();
void playRM();
}
public class AVIAdvancedVideo implements AdvancedVideo{
@Override
public void playAVI() {
System.out.println("播放AVI格式视频");
}
@Override
public void playWMV() {
}
@Override
public void playRM() {
}
}
public class WMVAdvancedVideo implements AdvancedVideo{
@Override
public void playAVI() {
}
@Override
public void playWMV() {
System.out.println("播放WMV格式视频");
}
@Override
public void playRM() {
}
}
public class RMAdvancedVideo implements AdvancedVideo{
@Override
public void playAVI() {
}
@Override
public void playWMV() {
}
@Override
public void playRM() {
System.out.println("播放RM格式视频");
}
}
// 定义适配器
public class VideoAdapter implements Video{
private AdvancedVideo advancedVideo;
public VideoAdapter(String format) {
if (format.equals("AVI")){
this.advancedVideo = new AVIAdvancedVideo();
}
else if (format.equals("WMV")){
this.advancedVideo = new WMVAdvancedVideo();
}
else if (format.equals("RM")){
this.advancedVideo = new RMAdvancedVideo();
}else {
// 按照业务进行不同处理
}
}
@Override
public void play(String format) {
if (format.equals("AVI")){
advancedVideo.playAVI();
}else if (format.equals("WMV")){
advancedVideo.playWMV();
}else if (format.equals("RM")){
advancedVideo.playRM();
}else {
// 按照业务进行不同处理
}
}
}
// 基础播放器支持其他格式
public class CommonVideo implements Video{
@Override
public void play(String format) {
if (format.equals("MP4")){
System.out.println("播放MP4格式视频");
}else if (format.equals("AVI") || format.equals("WMV") || format.equals("RM")){
new VideoAdapter(format).play(format);
}else {
System.out.println("不支持的播放格式");
}
}
public static void main(String[] args) {
CommonVideo commonVideo = new CommonVideo();
commonVideo.play("MP4");
commonVideo.play("AVI");
commonVideo.play("WMV");
commonVideo.play("RM");
}
}
它的缺点也很明显,如果新增播放格式,要修改很多类。
2.4 桥接模式
抽象化与实现化解耦,使得二者可以独立变化。属于结构型模式
public interface Person {
void doSomething();
}
public class Programmer implements Person{
@Override
public void doSomething() {
System.out.println("我是程序员,我会写代码");
}
}
public class Accountant implements Person {
@Override
public void doSomething() {
System.out.println("我是会计,我会统计财务消息");
}
}
// 对外的对象,提供调用者
public abstract class Worker {
protected Person person;
public Worker(BridgePattern.Person per) {
this.person = per;
}
public abstract void doSome();
}
// 桥接对象
public class PersonBridge extends Worker{
public PersonBridge(BridgePattern.Person per) {
super(per);
}
@Override
public void doSome() {
person.doSomething();
}
}
public class Test {
public static void main(String[] args) {
// PersonBridge只依赖抽象的Person,不管实现者是谁
Worker accountant = new PersonBridge(new Accountant());
accountant.doSome();
Worker programmer = new PersonBridge(new Programmer());
programmer.doSome();
}
}
2.5 装饰器模式
允许向一个现有的对象添加新的功能,同时又不改变其结构。属于结构型模式
对一个普通的火锅进行不同的装饰。
// 火锅
public interface HotPot {
void dooo();
}
public class BaseHotPot implements HotPot{
@Override
public void dooo() {
System.out.println("来个底料");
}
}
public class Lettuce implements HotPot {
private HotPot hotPot;
public Lettuce(HotPot hotPot) {
this.hotPot = hotPot;
}
@Override
public void dooo() {
hotPot.dooo();
System.out.println("要一斤生菜");
}
}
public class Onion implements HotPot {
private HotPot hotPot;
public Onion(HotPot hotPot) {
this.hotPot = hotPot;
}
@Override
public void dooo() {
hotPot.dooo();
System.out.println("要一斤洋葱");
}
}
public class Garlic implements HotPot {
private HotPot hotPot;
public Garlic(HotPot hotPot) {
this.hotPot = hotPot;
}
@Override
public void dooo() {
hotPot.dooo();
System.out.println("要一斤蒜");
}
}
public class Test {
public static void main(String[] args) {
// 只要火锅底料
HotPot baseHotPot = new BaseHotPot();
baseHotPot.dooo();
System.out.println();
// 要火锅底料+蒜
HotPot garlic = new Garlic(baseHotPot);
garlic.dooo();
System.out.println();
// 要火锅底料+蒜+生菜
HotPot lettuce = new Lettuce(garlic);
lettuce.dooo();
System.out.println();
// 要火锅底料+生菜+洋葱
HotPot lettuce1 = new Lettuce(baseHotPot);
HotPot onion = new Onion(lettuce1);
onion.dooo();
}
}
// 输出
来个底料
来个底料
要一斤蒜
来个底料
要一斤蒜
要一斤生菜
来个底料
要一斤生菜
要一斤洋葱
2.6 责任链模式
为请求创建了一个接收者对象的链。 属于行为型模式
public interface Teacher {
void correct(String job);
}
public class MathTeacher implements Teacher{
@Override
public void correct(String job) {
if (job.contains("数学作业")){
System.out.println("我是数学老师,批改数学作业");
}
}
}
public class ChineseTeacher implements Teacher{
@Override
public void correct(String job) {
if (job.contains("语文作业")){
System.out.println("我是语文老师,批改语文作业");
}
}
}
public class PhysicsTeacher implements Teacher{
@Override
public void correct(String job) {
if (job.contains("物理作业")){
System.out.println("我是物理老师,批改物理作业");
}
}
}
public class HistoryTeacher implements Teacher{
@Override
public void correct(String job) {
if (job.contains("历史作业")){
System.out.println("我是历史老师,批改历史作业");
}
}
}
public class TeacherChain {
private static ArrayList<Teacher> teachers;
static {
teachers = new ArrayList<>();
teachers.add(new HistoryTeacher());
teachers.add(new MathTeacher());
teachers.add(new ChineseTeacher());
teachers.add(new PhysicsTeacher());
}
public static void correctJob(String job){
for (Teacher teacher : teachers){
teacher.correct(job);
}
}
}
public class Test {
public static void main(String[] args) {
TeacherChain.correctJob("数学作业|语文作业");
System.out.println();
TeacherChain.correctJob("数学作业|历史作业|物理作业");
}
}
我是数学老师,批改数学作业
我是语文老师,批改语文作业
我是历史老师,批改历史作业
我是数学老师,批改数学作业
我是物理老师,批改物理作业
2.7 模板模式
一个抽象类公开定义了执行它的方法的方式/模板。属于行为型模式
public abstract class Person {
// 模板方法
protected void eat(){
System.out.println("吃饭");
}
protected abstract void doooo();
}
public class Teacher extends Person {
@Override
protected void doooo() {
System.out.println("上课");
}
}
public class Student extends Person {
@Override
protected void doooo() {
System.out.println("学习");
}
}
public class Test {
public static void main(String[] args) {
Student student = new Student();
student.eat(); // 公共的行为,可以共享,提供一个模板
student.doooo();
Teacher teacher = new Teacher();
teacher.eat(); // 公共的行为,可以共享,提供一个模板
teacher.doooo();
}
}
提供一个模板,可以共用。
总结
一起进步,一起写出更优秀的代码。