登录
注册
开源
企业版
高校版
搜索
帮助中心
使用条款
关于我们
开源
企业版
高校版
私有云
模力方舟
AI 队友
登录
注册
3月21日 深圳|OpenClaw 线下实战沙龙:招聘、资讯、项目协同三大场景实操,VS ZeroClaw 横向对比评测,别再只会装,来现场跑通真实业务!
代码拉取完成,页面将自动刷新
捐赠
捐赠前请先登录
取消
前往登录
扫描微信二维码支付
取消
支付完成
支付提示
将跳转至支付宝完成支付
确定
取消
Watch
不关注
关注所有动态
仅关注版本发行动态
关注但不提醒动态
24
Star
55
Fork
2
Java技术交流
/
Java技术提升库
代码
Issues
56
Pull Requests
0
Wiki
统计
流水线
服务
JavaDoc
PHPDoc
质量分析
Jenkins for Gitee
腾讯云托管
腾讯云 Serverless
悬镜安全
阿里云 SAE
Codeblitz
SBOM
我知道了,不再自动展开
更新失败,请稍后重试!
移除标识
内容风险标识
本任务被
标识为内容中包含有代码安全 Bug 、隐私泄露等敏感信息,仓库外成员不可访问
013、谈谈你知道的设计模式?
待办的
#I1VYQW
wgy
成员
创建于
2020-09-20 20:11
设计模式是人们为软件开发中相同表征的问题,抽象出的可重复利用的解决方案。在某种程度上,设计模式已经代表了ー些特定情況的最佳实践,同时也起到了软件工程师之间沟通的“行话”的作用。理解和掌握典型的设计模式,有利于我们提高沟通、设计的效率和质量。 今天我要问你的问题是,谈谈你知道的设计模式?请手动实现单例模式, Spring等框架中使用了哪些模式? ### 考点分析! 这个问题主要是考察对设计模式的了解和掌握程度!我们可以举例,更加清晰的说明典型设计模式到底长什么样子,典型使用场景是怎杨的。 我们以IO框架为例,大家直到InputStream是一个抽象类,标准类库中提供了FileInputStream、ByteArrayInputStream等各种不同的子类,分别从不同角度对InputStream进行了功能扩展,这是典型的装饰器模式应用案例。 识别装饰器模式,可以通过识别类设计特征来进行判断,也就是其类构造函数以相同的抽象类或者接口为输入参数。 因为装饰器模式本质上是包装同类型实例,我们对目标对象的调用,往往会通过包装类覆盖过的方法,迂回调用被包装的实例,这就可以很自然地实现增加额外逻辑的目的,也就是所谓的"装饰"。 例如, BufferedInputStream经过包装,为输入流过程增加缓存,类似这种装饰器还可以多次嵌套,不断地増加不同层次的功能。 ``` public BufferedInputStream(InputStream in) ``` 我在下面的类图里,简单总结了InputStream的装饰模式实践。  接下来再看第二个例子。创建型模式尤其是工厂模式,在我们的代码中随处可见,我举个相对不同的API设计实践。比如,JDK最新版本中HTTP/2 Client API,下面这个创建HttpRequest的过程,就是典型的构建器模式(Builder),通常会被实现成fluent风格的API,也有人叫它方法链。 ``` Httprequestrequest=httprequest.newbuilder(newURI(URI)) .header(headeralice, valuealice) .headers(headerbob, value1bob headerCarl, valuecarl, headerbob, value2bob) .get() .build(); ``` 使用构建器模式,可以比较优雅地解决构建复杂对象的麻烦,这里的"复杂"是指类似需要输入的参数组合较多,如果用构造函数,我们往往需要为每一种可能的输入参数组合实现相应的构造函数,一系列复杂的构造函数会让代码阅读性和可维护性变得很差。 上面的分析也进一步反映了创建型模式的初衷,即,将对象创建过程单独抽象出来,从结构上把对象使用逻辑和创建逻辑相互独立,隐藏对象实例的细节,进而为使用者实现了更加规范、统一的逻辑! 更进一步进行设计模式考察,面试官可能会: - 希望你写一个典型的设计模式实现。这虽然看似简单,但即使是最简单的单例,也能够综合考察代码基本功。 - 考察典型的设计模式使用,尤其是结合标准库或者主流开源框架,考察你对业界良好实践的掌握程度。 在面试时如果恰好问到你不熟悉的模式,你可以稍微引导一下,比如介绍你在产品中使用了什么自己相对熟悉的模式,试图解决什么问题,它们的优点和缺点等。 下面,我会针对前面两点,结合代码实例进行分析。 ### 典型回答! 大致按照模式的应用目标分类,设计模式可以分为创建型模式、结构型模式和行为型模式。 - 创建型模式,是对对象创建过程的各种问题和解决方案的总结,包括各种工厂模式(Factory、Abstract Factory)、单例模式(Singleton)、构建器模式(Builder)、原型模式( Prototype)。 - 结构型模式,是针对软件设计结构的总结,关注于类、对象继承、组合方式的实践经验。常见的结构型模式,包括桥接模式( Bridge)、适配器模式( Adapter)、装饰者模式(Decorator)、代理模式(Proxy)、组合模式(Composite)、外观模式(Facade)、享元模式( Flyweight)等。 - 行为型模式,是从类或对象之间交互、职责划分等角度总结的模式。比较常见的行为型模式有策略模式( Strategy)、解释器模式( Interpreter)、命令模式( Command)、观察者模式( Observer)、送代器模式( Iterator)、模板方法模式( Template Method)、访问者模式( Visitor)。 ### 知识扩展! 我们来实现一个日常非常熟悉的单例设计模式。看起来似乎很简单,那么下面这个样例符合基本需求吗? ``` public class Singleton{ private static Singleton instance; private Singleton(){} public static Singleton getinstance()t if(instance = null){ instance =new Singleton(); } return instance; } ``` 这个实现在单线程环境不存在问题,但是如果处于并发场景,就需要考虑线程安全,最熟悉的就莫过于"双检锁”,其要点在于: - 这里的volatile能够提供可见性,以及保证getInstance返回的是初始化完全的对象。 - 在同步之前进行null检查,以尽量避免进入相对昂贵的同步块。 - 直接在class级别进行同步,保证线程安全的类方法调用。 ``` public class Singleton{ private volatile static Singleton instance; private Singleton(){} public static Singleton getinstance() if(instance == null){ //尽量避免进入同步代码块,提高效率 synchronized(Singleton.class){ //使用类对象作为锁 if(instance == null){ //空检查 instance =new Singleton(); } } } return instance; } ``` 在这段代码中,争论较多的是volatile修饰静态变量,当 Singleton类本身有多个成员变时,需要保证初始化过程完成后,才能被get到。 在现代Java中,内存排序模型(JMM)已经非常完善,通过 volatile的 write或者read,能保证所谓的 happen- before,也就是避免常被提到的指令重排。换句话说,构造对象的store指令能够被保证一定在volatile read之前。 当然,也有ー些人推荐利用内部类持有静态对象的方式实现,其理论依据是对象初始化过程中隐含的初始化锁,这种和前面的双检锁实现都能保证线程安全,不过语法稍显晦涩,未必有特别的优势。 ``` public class Singleton{ private Singleton(){} public static Singleton getSingleton(){ return Holder.singleton; } private static class Holder{ private static Singleton singleton=new Singleton(); } } ``` 上面是比较学究的考察,其实实践中未必需要如此复杂,如果我们看Java核心类库自己的单例实现,比如ava.lang.Runtime,你会发现它并没使用复杂的双检锁之类静态实例被声明为final,这是被通常实践忽略的,一定程度保证了实例不被算改,也有限的保证执行顺序的语义。 ``` private static final Runtime currentRuntime=new Runtime(); private static Version version; // // public static Runtime getRuntime(){ return currentRuntime; } /**don't let anyone else instantiate this class **/ private Runtime(){} ``` 前面说了不少代码实践,下面一起来简要看看主流开源框架,如Spring等如何在API设计中使用设计模式。你至少要有个大体的印象,如: - BeanFactory和Application Context应用了工厂模式。 - 在Bean的创建中,Spring也为不同scope定义的对象,提供了单例和原型等模式实现。 - 在AOP领域则是使用了代理模式、装饰器模式、适配器模式等。 - 各种事件监听器,是观察者模式的典型应用。 - 类似JdbcTemplate等则是应用了模板模式。
设计模式是人们为软件开发中相同表征的问题,抽象出的可重复利用的解决方案。在某种程度上,设计模式已经代表了ー些特定情況的最佳实践,同时也起到了软件工程师之间沟通的“行话”的作用。理解和掌握典型的设计模式,有利于我们提高沟通、设计的效率和质量。 今天我要问你的问题是,谈谈你知道的设计模式?请手动实现单例模式, Spring等框架中使用了哪些模式? ### 考点分析! 这个问题主要是考察对设计模式的了解和掌握程度!我们可以举例,更加清晰的说明典型设计模式到底长什么样子,典型使用场景是怎杨的。 我们以IO框架为例,大家直到InputStream是一个抽象类,标准类库中提供了FileInputStream、ByteArrayInputStream等各种不同的子类,分别从不同角度对InputStream进行了功能扩展,这是典型的装饰器模式应用案例。 识别装饰器模式,可以通过识别类设计特征来进行判断,也就是其类构造函数以相同的抽象类或者接口为输入参数。 因为装饰器模式本质上是包装同类型实例,我们对目标对象的调用,往往会通过包装类覆盖过的方法,迂回调用被包装的实例,这就可以很自然地实现增加额外逻辑的目的,也就是所谓的"装饰"。 例如, BufferedInputStream经过包装,为输入流过程增加缓存,类似这种装饰器还可以多次嵌套,不断地増加不同层次的功能。 ``` public BufferedInputStream(InputStream in) ``` 我在下面的类图里,简单总结了InputStream的装饰模式实践。  接下来再看第二个例子。创建型模式尤其是工厂模式,在我们的代码中随处可见,我举个相对不同的API设计实践。比如,JDK最新版本中HTTP/2 Client API,下面这个创建HttpRequest的过程,就是典型的构建器模式(Builder),通常会被实现成fluent风格的API,也有人叫它方法链。 ``` Httprequestrequest=httprequest.newbuilder(newURI(URI)) .header(headeralice, valuealice) .headers(headerbob, value1bob headerCarl, valuecarl, headerbob, value2bob) .get() .build(); ``` 使用构建器模式,可以比较优雅地解决构建复杂对象的麻烦,这里的"复杂"是指类似需要输入的参数组合较多,如果用构造函数,我们往往需要为每一种可能的输入参数组合实现相应的构造函数,一系列复杂的构造函数会让代码阅读性和可维护性变得很差。 上面的分析也进一步反映了创建型模式的初衷,即,将对象创建过程单独抽象出来,从结构上把对象使用逻辑和创建逻辑相互独立,隐藏对象实例的细节,进而为使用者实现了更加规范、统一的逻辑! 更进一步进行设计模式考察,面试官可能会: - 希望你写一个典型的设计模式实现。这虽然看似简单,但即使是最简单的单例,也能够综合考察代码基本功。 - 考察典型的设计模式使用,尤其是结合标准库或者主流开源框架,考察你对业界良好实践的掌握程度。 在面试时如果恰好问到你不熟悉的模式,你可以稍微引导一下,比如介绍你在产品中使用了什么自己相对熟悉的模式,试图解决什么问题,它们的优点和缺点等。 下面,我会针对前面两点,结合代码实例进行分析。 ### 典型回答! 大致按照模式的应用目标分类,设计模式可以分为创建型模式、结构型模式和行为型模式。 - 创建型模式,是对对象创建过程的各种问题和解决方案的总结,包括各种工厂模式(Factory、Abstract Factory)、单例模式(Singleton)、构建器模式(Builder)、原型模式( Prototype)。 - 结构型模式,是针对软件设计结构的总结,关注于类、对象继承、组合方式的实践经验。常见的结构型模式,包括桥接模式( Bridge)、适配器模式( Adapter)、装饰者模式(Decorator)、代理模式(Proxy)、组合模式(Composite)、外观模式(Facade)、享元模式( Flyweight)等。 - 行为型模式,是从类或对象之间交互、职责划分等角度总结的模式。比较常见的行为型模式有策略模式( Strategy)、解释器模式( Interpreter)、命令模式( Command)、观察者模式( Observer)、送代器模式( Iterator)、模板方法模式( Template Method)、访问者模式( Visitor)。 ### 知识扩展! 我们来实现一个日常非常熟悉的单例设计模式。看起来似乎很简单,那么下面这个样例符合基本需求吗? ``` public class Singleton{ private static Singleton instance; private Singleton(){} public static Singleton getinstance()t if(instance = null){ instance =new Singleton(); } return instance; } ``` 这个实现在单线程环境不存在问题,但是如果处于并发场景,就需要考虑线程安全,最熟悉的就莫过于"双检锁”,其要点在于: - 这里的volatile能够提供可见性,以及保证getInstance返回的是初始化完全的对象。 - 在同步之前进行null检查,以尽量避免进入相对昂贵的同步块。 - 直接在class级别进行同步,保证线程安全的类方法调用。 ``` public class Singleton{ private volatile static Singleton instance; private Singleton(){} public static Singleton getinstance() if(instance == null){ //尽量避免进入同步代码块,提高效率 synchronized(Singleton.class){ //使用类对象作为锁 if(instance == null){ //空检查 instance =new Singleton(); } } } return instance; } ``` 在这段代码中,争论较多的是volatile修饰静态变量,当 Singleton类本身有多个成员变时,需要保证初始化过程完成后,才能被get到。 在现代Java中,内存排序模型(JMM)已经非常完善,通过 volatile的 write或者read,能保证所谓的 happen- before,也就是避免常被提到的指令重排。换句话说,构造对象的store指令能够被保证一定在volatile read之前。 当然,也有ー些人推荐利用内部类持有静态对象的方式实现,其理论依据是对象初始化过程中隐含的初始化锁,这种和前面的双检锁实现都能保证线程安全,不过语法稍显晦涩,未必有特别的优势。 ``` public class Singleton{ private Singleton(){} public static Singleton getSingleton(){ return Holder.singleton; } private static class Holder{ private static Singleton singleton=new Singleton(); } } ``` 上面是比较学究的考察,其实实践中未必需要如此复杂,如果我们看Java核心类库自己的单例实现,比如ava.lang.Runtime,你会发现它并没使用复杂的双检锁之类静态实例被声明为final,这是被通常实践忽略的,一定程度保证了实例不被算改,也有限的保证执行顺序的语义。 ``` private static final Runtime currentRuntime=new Runtime(); private static Version version; // // public static Runtime getRuntime(){ return currentRuntime; } /**don't let anyone else instantiate this class **/ private Runtime(){} ``` 前面说了不少代码实践,下面一起来简要看看主流开源框架,如Spring等如何在API设计中使用设计模式。你至少要有个大体的印象,如: - BeanFactory和Application Context应用了工厂模式。 - 在Bean的创建中,Spring也为不同scope定义的对象,提供了单例和原型等模式实现。 - 在AOP领域则是使用了代理模式、装饰器模式、适配器模式等。 - 各种事件监听器,是观察者模式的典型应用。 - 类似JdbcTemplate等则是应用了模板模式。
评论 (
0
)
登录
后才可以发表评论
状态
待办的
待办的
进行中
已完成
已关闭
负责人
未设置
wgy
wgy2018
负责人
协作者
+负责人
+协作者
标签
未设置
标签管理
里程碑
01.JavaSE阶段面试题
未关联里程碑
Pull Requests
未关联
未关联
关联的 Pull Requests 被合并后可能会关闭此 issue
分支
未关联
未关联
master
开始日期   -   截止日期
-
置顶选项
不置顶
置顶等级:高
置顶等级:中
置顶等级:低
优先级
不指定
严重
主要
次要
不重要
参与者(1)
1
https://gitee.com/beike-java-interview-alliance/java-interview.git
git@gitee.com:beike-java-interview-alliance/java-interview.git
beike-java-interview-alliance
java-interview
Java技术提升库
点此查找更多帮助
搜索帮助
Git 命令在线学习
如何在 Gitee 导入 GitHub 仓库
Git 仓库基础操作
企业版和社区版功能对比
SSH 公钥设置
如何处理代码冲突
仓库体积过大,如何减小?
如何找回被删除的仓库数据
Gitee 产品配额说明
GitHub仓库快速导入Gitee及同步更新
什么是 Release(发行版)
将 PHP 项目自动发布到 packagist.org
评论
仓库举报
回到顶部
登录提示
该操作需登录 Gitee 帐号,请先登录后再操作。
立即登录
没有帐号,去注册