深入理解设计模式

本文深入探讨设计模式,包括前言、六大原则,并详细解析简单工厂、工厂方法、抽象工厂、单例模式以及适配器模式。通过实例阐述各种模式的核心思想和应用场景,旨在帮助读者理解如何在实际项目中灵活运用设计模式,提高代码的可读性和可扩展性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【前言】

        做了几个可大可小的项目,或多或少的接触了代码复用,代码解耦,代码高扩展等一系列优秀的代码的实用性,提高了代码的可读性,可扩展性,维护成本,复杂的业务问题。而我们生活上也是如此,如果一件事情,总要不断重复去做,虽然我们可以把事情做好,但是总是杂乱无章,这不免会浪费我们的时间成本,对生活深度分析,并且用代码实现这种思想,不断思考,思想与代码并驾齐驱,灵活运用设计模式,理清应该有的脉络,才更加有趣。最近在设计模式之禅里面看到作者提了一系列问题来引入学习设计模式的历程:这个定义是这样吗?是应该用抽象类还是用接口?为什么在这里不能抽取抽象呢?为什么在项目中这个模式要如此蜕化?相信我们都会有这样的疑问,这篇博客,我们拿出来几个典型的模式来做一下。

【六大原则】

单一职责原则:一个接口只干一件事情,例如一个接口定义了拨通电话和通话以及挂电话三个方法,
如果是按照打
电话这件事情来说,这三个方法可以放到一个接口中,如果按照另一种方式解释的话:它可以包含
两个职责:协议管理以及数据传送,通话为数据传送,拨通电话和挂电话为协议管理。

里氏转化原则:只要父类能出现的地方子类就可以出现,而且替换为子类也不会出现任何错误或异
常,使用者可能根本不需要知道是父类还是子类。但是,反过来就不行了,有子类出现的地方,
父类未必就能适应。

依赖倒置原则:模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是
通过接口活抽象类产生的;接口或抽象类不依赖于实现类;实现类依赖接口或抽象类。

接口隔离原则:建立单一接口,不建立臃肿庞大的接口。在符合单一职责原则上,进行接口隔离
原则。

迪米特法则:我的知识,你知道的越少越好  、

开闭原则:对扩展开发,对修改关闭       

  【简单工厂,工厂方法,抽象工厂】

    简单工厂核心代码:


public class SimpleFactory {

    public Milk getMilk(String name){
        if("特仑苏".equals(name)){
            return new Telunsu();
        }else if("伊利".equals(name)){
            return new Yili();
        }else if("蒙牛".equals(name)){
            return new Mengniu();
        }else {
            System.out.println("不能生产您所需的产品");
            return null;
        }
    }

}

当我们调用这个工厂的是时候,我们只需要把我们的需求告诉工厂。然后给我们返回结果。相当于小作坊。统一管理所有的事情,结合生活,哆啦a梦的百宝箱是不存在的。spring中的bean也是如此,有很多种,我们不可能所有的都放到一起,体现不出专业性。因为专一,才能显得专业,这时候就出现了工厂方法:


public interface Factory {

    //工厂必然具有生产产品技能,统一的产品出口
    Milk getMilk();

}


public class MengniuFactory implements  Factory {


    @Override
    public Milk getMilk() {
        return new Mengniu();
    }
}

public class SanluFactory implements  Factory {
    @Override
    public Milk getMilk() {
        return new Sanlu();
    }
}

public class TelunsuFactory implements Factory {

    @Override
    public Milk getMilk() {
        return new Telunsu();
    }
}

这时候我们就可以发现,每一种牛奶都有各自牛奶的品牌的厂商来生产,显得专业了吧。但是我们再想一想,如果我需要特仑苏,我得需要去特仑苏厂商取,如果需要蒙牛,得去蒙牛取,在加如一家厂商,我也需要在添加一个工厂,这样也会麻烦的。这时候我们需要用抽象工厂了。


/**
 *
 * 抽象工厂是用户的主入口
 * 在Spring中应用得最为广泛的一种设计模式
 * 易于扩展
 */
public abstract class AbstractFactory {

    //公共的逻辑
    //方便于统一管理

    /**
     * 获得一个蒙牛品牌的牛奶
     * @return
     */
    public  abstract Milk getMengniu();

    /**
     * 获得一个伊利品牌的牛奶
     * @return
     */
    public abstract  Milk getYili();

    /**
     * 获得一个特仑苏品牌的牛奶
     * @return
     */
    public  abstract  Milk getTelunsu();

    public abstract Milk getSanlu();

}

public class MilkFactory extends  AbstractFactory {


    @Override
    public Milk getMengniu() {
        return new Mengniu();
    }

    @Override
    public Milk getYili() {
        return new Yili();
    }

    @Override
    public Milk getTelunsu() {
        return new Telunsu();
    }

    @Override
    public Milk getSanlu() {
        return new Sanlu();
    }
}

这时候你如果想要牛奶,你只需要告诉牛奶工厂就行,再一个,对于用户来说,你如果添加一个牛奶品牌,只需要再牛奶工厂进行添加就行,具体的内部修改放到抽象类里面,很好的符合了开放封闭原则,对扩展开放,对修改关闭

【单例模式】

保证一个类只允许有一个实例,例如后端配置文件

具体实现的方式有:饿汉式单例模式,懒汉式单例模式,注册登记式,枚举式,序列化与反序列化

饿汉式单例模式
package singleton.hungry;

/**
 * Created by 张伟光 on 2019/6/3.
 * 懒汉式
 */
public class Hungry {
    private Hungry(){}
    //类加载机制,先静态,后动态,先属性,后方法
    private static final Hungry hungry = new Hungry();
    public static Hungry getInstance() {

        return hungry;
    }

}

性能还可以,但是类加载的时候就初始化了,不管你占不占用。下面看一下懒汉式单例模式


public class LazyOne {
    private LazyOne(){}

    private static LazyOne lazy = null;

    public static LazyOne getInstance() {
        if(lazy == null) {
            lazy = new LazyOne();
        }
        return lazy;
    }
}

如果存在并发的情况下这种,会出现两个对象,不太可靠。如果给这个方法加个锁,变成同步的话,就又会出现时间延迟的问题,性能会差很多。这时候又想到了其它的方法

package singleton.lazy;

/**
 * Created by 张伟光 on 2019/6/3.
 */
public class LazyThree {
    private static boolean initialized = false;
    //默认使用LazyThree 的时候,会先初始化内部类
    //如果没使用的话,内部类是不加载的

    private LazyThree(){
        synchronized (LazyThree.class) {
            if(initialized == false) {
                initialized = !initialized;

            }else {
                throw new RuntimeException("单例已被侵犯");
            }
        }
    }
    public static final LazyThree getInstance() {
        return lazyHolder.LAZY;
    }

    private static class lazyHolder {
        private static final LazyThree LAZY = new LazyThree();

    }
}

我们通过静态内部类,通过调用内部类来进行实例化的方法保证单一。被称为史上最牛B的单例模式的实现方式。

再提一个在spring中常用的设计模式注册式单例模式

package singleton.register;

import java.util.HashMap;
import java.util.Map;

/**
 * Created by 张伟光 on 2019/6/3.
 */
public class RegisterMap {
    private static Map<String,Object> register = new HashMap<String,Object>();

    public static RegisterMap getInstance(String name){
        if(name == null){
            name = RegisterMap.class.getName();
        }
        if(register.get(name) == null) {
            try {
                register.put(name,new RegisterMap());
            } catch (Exception e){
                e.printStackTrace();
            }
        }
        return (RegisterMap)register.get(name);
    }
}

将生成单例放到map中,下次实例化的时候先判断map中是否已经存在这个对象。其它的单例模式就不赘述了。

【适配器模式】

适配器模式主要是为了兼容,例如老系统只能用一般输入账号密码的方式进行登录,现如今加了微信登录,qq登录,手机验证码等登录方式,我们这个时候要兼容原先的登录方式,我们需要新添加一个类。


public class SiginService {

    /**
     * 注册方法
     * @param username
     * @param password
     * @return
     */
    public ResultMsg regist(String username,String password){
        return  new ResultMsg(200,"注册成功",new Member());
    }


    /**
     * 登录的方法
     * @param username
     * @param password
     * @return
     */
    public ResultMsg login(String username,String password){
        return null;
    }

}

public class SiginForThirdService extends SiginService {

    public ResultMsg loginForQQ(String openId){
        //1、openId是全局唯一,我们可以把它当做是一个用户名(加长)
        //2、密码默认为QQ_EMPTY
        //3、注册(在原有系统里面创建一个用户)

        //4、调用原来的登录方法

        return loginForRegist(openId,null);
    }

    public ResultMsg loginForWechat(String openId){
        return null;
    }

    public ResultMsg loginForToken(String token){
        //通过token拿到用户信息,然后再重新登陆了一次
        return  null;
    }

    public ResultMsg loginForTelphone(String telphone,String code){

        return null;
    }

    public ResultMsg loginForRegist(String username,String password){
        super.regist(username,null);
        return super.login(username,null);
    }

}

这个是为了兼容,而装饰器模式是动态的覆盖或者增加方法,两者是不一样的,但是可以说装饰器模式是一种非常特殊的适配器模式。

【总结】

其它设计模式就先不逐一介绍了,不过多少都有联系,设计模式并不是单一存在,也不是固定于某一种形式,当真正体会到其中的变得时候,可能才有意思。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值