• Java:一篇学好设计模式


    什么是设计模式

    简单理解,设计模式是前人多年写代码踩坑总结出来的优秀代码攻略,目的是减少大量无用代码,让项目更好维护

    七大设计原则

    接下来要讲的23种设计模式,但遵循下面的七大原则:

    1. 单一职责原则
      2、接口隔离原则
    2. 依赖倒置原则
      4、里氏替换原则
    3. 开闭原则
    4. 迪米特原则
    5. 合成复用原则

    单一职责原则

    单一职责原则,目的是每个类履行一种职责。好比一个人是医生,就好好干医生份内事,无需插手护士、保洁等和医生职责无关事

    反例

    比如说一个Person类,有工作work的方法:

    public class Person{
    	public void work(String person){
    		System.out.println("职位为:"+ person + "的人正在工作");
    	}
    }
    

    这个Person,根据传的person能做各种职位的事,破坏了单一职责

    正例

    public interface Person{
    	public void work();
    }
    
    public class Doctor implement Person{
    	public void work(){
    	}
    }
    
    public class Teacher implement Person{
    	public void work(){
    	}
    }
    

    接口隔离原则

    这个原则有点难讲,总结就是把接口拆成最简。

    反例

    有一个Person,有 fight打人、eat吃饭、work工作三个方法

    public interface Person{
    	public void fight();
    	public void eat();
    	public void work();
    }
    

    现在有两个类:PersonA 和 PersonB 都实现了Person接口,实际上PersonA根本不需要打人,PersonB不需要工作。

    正例

    public interface Person1{
    	public void eat();
    }
    
    public interface Person2 extend Person1{
    	public void fight();
    }
    
    public interface Person3 extend Person1{
    	public void work();
    }
    

    拆成3个接口。如此一来,PersonA只需要实现Person3,PersonAB实现Person2

    依赖倒置原则

    依赖倒置原则,核心在于面向接口编程,传细节,定抽象。

    反例

    有个Person类,有send方法。

    public class Person{
    	public void send(Email mail){
    		System.out.println(mail.getInfo());
    	}
    }
    

    这方法只能发邮件,那我发短信啥的就不行了

    正例

    定义3个接口

    public interface Receiver{
    	public void getInfo();
    }
    
    public class Email implement Receiver{
    	public void getInfo(){
    	
    	}
    }
    
    public class WeiXin  implement Receiver{
    	public void getInfo(){
    	
    	}
    }
    

    结果:

    public class Person{
    	public void send(Receiver r){
    		System.out.println(mail.getInfo());
    	}
    }
    

    里氏替换原则

    里氏替换原则,他的核心在于:尽量子类不用去重写父类的方法。

    开闭原则

    开闭原则,核心在于:对外允许拓展,对内拒绝修改。
    什么意思呢?比如现在我给你了空的羽毛球圆筒,对外只需要装合适的羽毛球即可,对内是不拒绝像把羽毛球筒硬搞成方的

    再举个例子,一些配置文件有些参数是固定好的,比如 server.port。对内可以编写自己的参数,对内是不允许修改server.port的参数名

    迪米特原则

    迪米特原则,被依赖的类尽量让依赖类知道的内部实现细节尽量少,总结就是降低耦合

    合成复用原则

    合成复用原则,当使用另一个类的方法时,尽量使用聚合的方式,减少使用继承。

    这个比较好理解,现在有A类有个A1方法,B想要使用A1。不好的做法就是B去继承A,然后就有A1方法。推荐是B里引入A,然后调用A去用A1

    设计模式

    设计模式可分为3种类型,下面列举需要重点掌握的设计模式

    创建型模式:单例模式,工厂模式,原型模式,建造者模式
    结构型模式:适配器模式,装饰模式,代理模式
    行为型模式:观察者模式

    单例模式

    单例模式,要求在整个应用中一个类只有一个对象实例。
    刚开始此模式,懒汉和饿汉式的单例代码比较经典,大家有空去看下。

    然后在spring最常见的单例就是我们的组件,默认都是单例的,也是单例的体现。

    工厂模式

    简单工厂模式

    比如车有很多品牌:奥迪、宝马、保时捷...如果我要一辆宝马,那我得手动new,要一辆保时捷还是得new。那简单工厂模式,在这里可以定义一个创建车工厂类,传入参数拿到不同品牌车。如:

    Car car = CarFactory.getCar(String brand);
    

    抽象工厂模式

    抽象工厂模式是简单工厂模式的升级版。简单工厂模式从一个工厂拿不同牌子的车,那如果某个牌子中细分有车型,颜色....那单个工厂显然不够用了

    利用抽象工厂模式,可以从工厂方法A中拿到某个牌子的工厂B,再根据B拿到具体的车,如下:

    BrandCarFactory brandCarFactory = CarFactory.getBrandCarFactory(String brand);
    
    Car car = brandCarFactory.getCar(String color,String size....)
    

    原型模式

    原型模式,通过原型对象构建出和他属性一样的实例。比如说创建10只羊,每次羊都叫喜羊羊,18岁。当第一只创建后,第二只的创建属性都从第一支获取

    spring的bean配置为多例时,利用到的就是原型模式。

    建造者模式

    建造者模式,和工厂模式有点类似。说起来有点难讲,还是看下例子:

    1. 执行入口:
    public class Demo{
    	public static void main(String[] args) {
            HouseDiretor dir = new HouseDiretor(new HightHouse());
    		House house = dir.buildHouse();
        }
    }
    
    1. 各个类:
    //房子构造者
    public abstract class HouseBuilder{
    	private House house = new House();
    	
    	public void buildA();
    	public void buildB();
    	public void buildC();
    	
    	public void buildHouse(){
    		return house;
    	}
    }
    
    //高楼
    public class HightHouse extends HouseBuilder{
    	public void buildA(){
    	...
    	}
    	public void buildB(){
    	...
    	}
    	public void buildC(){
    	....
    	}
    	
    }
    
    //指挥者
    public class HouseDiretor{
    	private HouseBuilder builder;
    	
    	public HouseDiretor(HouseBuilder builder){
    		this.builder = builder;
    	}
    	
    	public void buildHouse(){
    		builder.buildA();
    		builder.buildB();
    		builder.buildC();
    		return builder.buildHouse();
    	}
    }
    

    适配器模式

    适配器模式,就很好理解了。
    平时大家见过的转接头就是适配器,比如你笔记本只有USB接口,但插口是HIDM,这就需要一个HIDM转USB的转换器充当连接。

    类适配器模式

    下面以充电器将220V转5V电压作为适配器来编写例子:
    5V接口:

    public interface Inter5V{
    	public int output5V();
    }
    

    220V实现类

    public class Impl220V {
    	public int output220V(){
    		return 220;
    	}
    }
    

    适配器类

    public class Impl5V extends Impl220V implement Inter5V{
    	public int output5V(){
    		return output220V()/24
    	}
    }
    

    比如现在手机要充电

    public class Phone{
    	public void charging(Inter5V inter5V){
    		if(inter5V.output5V == 5){
    		 //可以充电了
    		}else{
    			//不能充电
    		}
    	}
    }
    
        public static void main(String[] args) {
            Phone phone = new Phone();
    		phone.charging(new Impl55V)
        }
    

    对象适配器模式

    类适配器模式在于继承,这种方式有局限性。
    而对象适配器模式在于聚合,没有继承关系限制。还是按上面的例子,但是某些类发生变化:
    适配器类

    public class Impl5V implement Inter5V{
    	private Impl220V impl220V;;
    	
    	public Impl20V(Impl220V impl220V){
    		this.impl220V = impl220V;
    	}
    	public int output5V(){
    		return impl220V.output220V()/24
    	}
    }
    

    充电入口:

    public static void main(String[] args) {
            Phone phone = new Phone();
    		phone.charging(new Impl55V(new Impl220V()))
     }
    

    接口适配器模式

    接口适配器模式,和上面两种不太一样。他解决是这样一个问题:接口的方法不想全部实现,只实现自己要用的即可

    接口适配器往往会定义抽一个抽象类,去实现A接口的所有方法,但是方法体全为空。然后我们再去继承抽象类,选择性重写自己的方法即可。

    装饰者模式

    装饰者模式,在于解决银排列组合问题。就比如说牛肉面,排骨面...这些都是固定死的,理想状态是一碗面,想加啥自己加。
    在Java在装饰罩的体现,有个很重要的前提,就是被装饰着和装饰者都会实现或继承相同的父类,具体参考如下:
    https://blog.csdn.net/m0_47944994/article/details/127901010

    装饰者A a = new 装饰者A(new 被装饰者)
    装饰者B b = new 装饰者B(new 被装饰者)

    代理模式

    代理模式,在不改动A方法的基础上,代理类(增强类)对A方法进行前后的增强。

    代理模式可以分为三种:静态代理,动态代理(JDK代理,接口代理),cglib代理

    静态代理

    静态代理,有个重要的前提是:代理类和被代理类都要实现同一接口,直接上例子:
    同一接口:

    public interface ITeateacher{
    	public void teach();
    }
    

    被代理类:

    public class Teacher implments ITeateacher{
    	public void teach(){
    		System.out.println("教学生");
    	}
    }
    

    代理类:

    public class TeacherProxy implments ITeateacher{
    	public ITeateacher teacher;
    	
    	public TeacherProxy(ITeateacher teacher){
    		this.teacher = teacher;
    	}
    	public void teach(){
    		System.out.println("备课");
    		teacher.teacher();
    		System.out.println("下课");
    	}
    }
    

    使用:

    public static void main(String[] args) {
    	ITeateacher teacher = new TeacherProxy(nwe Teacher());
    	teacher.teach()
    }
    

    好处就不说了,就是方法增强了。但缺点是实现同一接口,后面接口方法进行拓展不好维护

    动态代理

    动态代理也被叫JDK代理,因为代理对象是依赖JDK的API来生成的,不需要我们去创建代理类。

    前提要求是目标类是要实现接口的,并且是对接口的所有方法进行增强,实例如下:

    目标类接口:

    public interface ITeacherDao {
        public String teach(String person);
    }
    

    目标类:

    public class TeacherDao implements ITeacherDao{
        @Override
        public String teach(String person) {
            return person + "在教书";
        }
    }
    

    代理工厂类,我们编写用于获取代理对象:

    public class ProxyFactory {
    	public ProxyFactory(Object targetObj){
            this.targetObj = targetObj;
        }
    	
    	public Object getProxyIntance(){
            	return Proxy.newProxyInstance(targetObj.getClass().getClassLoader(), targetObj.getClass().getInterfaces()
                    , new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            System.out.println("增强前代码");
                            Object returnValue = method.invoke(targetObj, args);
                            System.out.println(returnValue);
                            System.out.println("增强后代码");
                            return returnValue;
                        }
                    });
        }
    }
    

    使用:

    public static void main(String[] args) {
            ProxyFactory proxyFactory = new ProxyFactory(new TeacherDao());
    
            //获取代理对象(JDK帮我们生成的)
            ITeacherDao proxyIntance = (ITeacherDao) proxyFactory.getProxyIntance();
    
            proxyIntance.teach("张三");
        }
    

    动态代理生成代理对象其实也实现了接口,和静态一样。不一样是代理对象是JDK帮我们做了而已

    cglib代理

    cglib代理,如果说目标类是不需要实现任何接口的,那么就用不了动态代理,但是cglib代理可以解决

    注意cglib是一个框架包,需要咨询引入使用。下面直接上代码:
    目标类:

    public class TeacherDao{
    
        public String teach(String person) {
            return person + "在教书";
        }
    }
    

    代理工厂类:

    public class ProxyFactory implements MethodInterceptor {
        private Object targetObj;
    
        public ProxyFactory(Object targetObj){
            this.targetObj = targetObj;
        }
    
        public Object getProxyIntance(){
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(targetObj.getClass());//设置父类,cglib原理就是通过继承目标类来生成子类(代理类)
            enhancer.setCallback(this);
            return enhancer.create();
        }
    
        @Override
        public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            System.out.println("cglib增强,增强前----");
            Object returnValue = method.invoke(targetObj, args);
            System.out.println(returnValue);
            System.out.println("cglib增强,增强后----");
            return returnValue;
        }
    }
    

    使用

        public static void main(String[] args) {
            ProxyFactory proxyFactory = new ProxyFactory(new TeacherDao());
    
            //获取代理对象(JDK帮我们生成的)
            TeacherDao proxyIntance = (TeacherDao) proxyFactory.getProxyIntance();
    
            proxyIntance.teach("张三");
        }
    

    模板模式

    模板模式很简单,就是定义了一套流程所需的接口和并且子类实现。比如RedisTemplate,JdbcTemplate


    __EOF__

  • 本文作者: 爱编程DE文兄
  • 本文链接: https://www.cnblogs.com/ibcdwx/p/17302459.html
  • 关于博主: 评论和私信会在第一时间回复。或者直接私信我。
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
  • 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。
  • 相关阅读:
    Docker的安装
    2023/09/22 制作demo期间心得
    【Java初阶】- - - static 关键字
    git使用看这一篇就够了
    数控机床传动装置机械及PLC电气控制系统设计
    基于粒子群算法训练常规自动编码器附Matlab代码
    opensearch与elasticsearch对比
    Spring MVC中如何限制Controller为POST或GET方式接收参数呢?
    Vue 在同一个页面里在不同的router-view里展示页面信息
    Karmada调度器
  • 原文地址:https://www.cnblogs.com/ibcdwx/p/17302459.html