【Java 常用的设计模式】

目录

前言

一、设计原则

1.1 开闭原则

1.2 单一职责原则

1.3 依赖倒置原则

1.4 接口分离原则

1.5 迪米特法则

1.6 里氏替换原则

二、设计模式

2.1 单例模式

2.1.1 饿汉式

2.1.2 懒汉式非安全

2.1.3 懒汉式安全

2.1.4 double+check

2.1.5 静态内部类

2.1.6 枚举

2.2 建造者模式

2.3 适配器模式

2.4 桥接模式

2.5 装饰器模式

2.6 责任链模式

 2.7 模板模式

总结


前言

        合理的使用设计模式可以简化开发,提高代码的阅读量,让代码看起来更加的精简,代码结构层次更加的清晰,学习设计模式可以让我们的思维更加的灵活和抽象化。

        代码中从不使用设计模式的缺点是:代码看起来繁杂、凌乱、臃肿和代码占用大小增加,阅读性差,找不到阅读入口等等。

        但是如果不熟悉设计模式,没有深入的研究过,不了解它的核心思想,生搬硬套的使用设计模式,也会出现线程安全,资源泄露和代码性能降低等等问题。

        这篇文章就是以我的理解对常用的设计模式进行讲解,帮助读者快速的了解这些设计模式,但是怎么使用它,还是要看具体的场景。


一、设计原则

        简单的介绍一下六大设计原则,因为设计模式离不开这些原则。

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();
    }
}

        提供一个模板,可以共用。


总结

       一起进步,一起写出更优秀的代码。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

编程界~小卡拉米

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值