2024年Java最全SOLID:面向对象设计的五个基本原则,华为java校招面试题

最后

Java架构进阶面试及知识点文档笔记

这份文档共498页,其中包括Java集合,并发编程,JVM,Dubbo,Redis,Spring全家桶,MySQL,Kafka等面试解析及知识点整理

image

Java分布式高级面试问题解析文档

其中都是包括分布式的面试问题解析,内容有分布式消息队列,Redis缓存,分库分表,微服务架构,分布式高可用,读写分离等等!

image

互联网Java程序员面试必备问题解析及文档学习笔记

image

Java架构进阶视频解析合集

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

下面是一个Modem优化后的设计:

通过拆分猫的接口,我们可以在应用的其他部分将猫的设计分开来对待。虽然我们又在猫的实现中(Modem Implementation)将这两部分职责重新耦合在一起,但是除了初始化猫的代码以外,在使用面向接口编程的原则后,其他代码并不需要依赖于猫的实现。

SRP是最简单的一个面向对象设计原则,但也是最难做正确的一个,因为我们习惯于将职责合并,而不是将它们分开来。找到并且拆分这些职责正是软件设计真正需要做的事情。

小结一下单一职责原则就是:

**核心思想:**应该有且仅有一个原因引起类的变更

**好处:**类的复杂度降低、可读性提高、可维护性提高、扩展性提高、降低了变更引起的风险。

**需注意:**单一职责原则提出了一个编写程序的标准,用“职责”或“变化原因”来衡量接口或类设计得是否优良,但是“职责”和“变化原因”都是不可以度量的,因项目和环境而异。

开闭原则


开闭原则的英文是 Open Closed Principle,缩写为 OCP。

开闭原则说的是:软件实体(模块、类、函数等等)应该对扩展是开放的,对修改是关闭的。

  • 对扩展是开放的,意味着软件实体的行为是可扩展的,当需求变更的时候,可以对模块进行扩展,使其满足需求变更的要求。

  • 对修改是关闭的,意味着当对软件实体进行扩展的时候,不需要改动当前的软件实体;不需要修改代码;对于已经完成的类文件不需要重新编辑;对于已经编译打包好的模块,不需要再重新编译。

两者结合起来表述为:添加一个新的功能应该是,在已有代码基础上扩展代码(新增模块、类、方法等),而非修改已有代码(修改模块、类、方法等)。

这里我们以出售电脑为例,首先定义一个顶层接口Computer,然后定义两个实现类,华硕电脑与苹果Mac,类层次结构如下图所示:

上面是我们一开始的需求,但是随着软件发布运行,我们的需求不可能一成不变,肯定要接轨市场。假设现在是双十一,华硕笔记本电脑需要搞促销活动。那么我们的代码肯定要添加新的功能。可能有些刚入职的新人会在原有的代码上做改动,这肯定不符合开闭原则,虽然这种做法最直接,也最简单,但是绝大部分项目中,一个功能的实现远比想像要复杂的多,我们在原有的代码中进行修改,其风险远比扩展和实现一个方法要大的多。正确的做法可以这样:

我们实现一个关于折扣的子类,其中包含一个关于折扣的方法,这方法相当于一个扩展方法。可以看到这个子类是AsusComputer的,那为什么不把他设计成一个共用的折扣类呢,比如DiscountComputer,所有实现类都继承这个折扣类。这是因为每种实现类的折扣方案可能是不一样的。所以我们最好能把它作为每个实现类的子类单独实现。如果你能确保你的业务中的新功能能兼容所有相关联的需求你也可以共用一个。

小结一下开闭原则就是:

**核心思想:**尽量通过扩展软件实体来解决需求变化,而不是通过修改已有的代码来完成变化

**通俗来讲:**一个软件产品在生命周期内,都会发生变化,既然变化是一个既定的事实,我们就应该在设计的时候尽量适应这些变化,以提高项目的稳定性和灵活性。

里氏替换原则


里氏替换原则由Barbara Liskov提出,这个原则很明显,Java的多态或者C++的虚函数本身就允许把指向基类的指针或引用,在调用其方法或函数的时候,调用实际类型的方法或函数。我们来看一个简单的例子:Circle 和 Square 继承了基类 Shape,然后在应用的方法中,根据输入 Shape 对象类型进行判断,根据对象类型选择不同的绘图函数将图形画出来。

void drawShape(Shape shape) {

if (shape.type == Shape.Circle ) {

drawCircle((Circle) shape);

} else if (shape.type == Shape.Square) {

drawSquare((Square) shape);

} else {

……

}

}

这种写法的代码既常见又糟糕,它同时违反了开闭原则和里氏替换原则。

  • 首先看到这样的 if/else 代码,就可以判断违反了(我们刚刚在上个部分讲过的)开闭原则:当增加新的 Shape 类型的时候,必须修改这个方法,增加 else if 代码。

  • 其次也因为同样的原因违反了里氏替换原则:当增加新的Shape 类型的时候,如果没有修改这个方法,没有增加 else if 代码,那么这个新类型就无法替换基类 Shape。

要解决这个问题其实也很简单,只需要在基类 Shape 中定义 draw 方法,所有 Shape 的子类,Circle、Square 都实现这个方法就可以了:

public abstract Shape{

public abstract void draw();

}

上面那段 drawShape() 代码也就可以变得更简单:

void drawShape(Shape shape) {

shape.draw();

}

这段代码既满足开闭原则:增加新的类型不需要修改任何代码。也满足里氏替换原则:在使用基类的这个方法中,可以用子类替换,程序正常运行。

小结一下里氏替换原则就是:

**核心思想:**在使用基类的的地方可以任意使用其子类,能保证子类完美替换基类。

**通俗来讲:**只要父类能出现的地方子类就能出现。反之,父类则未必能胜任。

**好处:**增强程序的健壮性,即使增加了子类,原有的子类还可以继续运行。

需注意:如果子类不能完整地实现父类的方法,或者父类的某些方法在子类中已经发生“畸变”,则建议断开父子继承关系 采用依赖、聚合、组合等关系代替继承。

接口隔离原则


接口隔离原则的英文是 SInterface Segregation Principle,缩写为 ISP。这个原则是说:客户端不应该强迫依赖它不需要的接口。

我们在设计微服务或者类库接口的时候,如果部分接口只被部分调用者使用,那我们就需要将这部分接口隔离出来,单独给对应的调用者使用,而不是强迫其他调用者也依赖这部分不会被用到的接口。举一个简单的例子:

public interface UserService {

boolean register(String cellphone, String password);

boolean login(String cellphone, String password);

UserInfo getUserInfoById(long id);

UserInfo getUserInfoByCellphone(String cellphone);

}

public interface RestrictedUserService {

boolean deleteUserByCellphone(String cellphone);

boolean deleteUserById(long id);

}

public class UserServiceImpl implements UserService, RestrictedUserService {

// …省略实现代码…

}

删除用户是一个非常慎重的操作,我们只希望通过后台管理系统来执行,所以这个接口只限于给后台管理系统使用。如果我们把它放到 UserService 中,那所有使用到 UserService 的系统,都可以调用这个接口。不加限制地被其他业务系统调用,就有可能导致误删用户。

参照接口隔离原则,调用者不应该强迫依赖它不需要的接口,将删除接口单独放到另外一个接口RestrictedUserService 中,然后将 RestrictedUserService 只打包提供给后台管理系统来使用。

小结一下接口隔离原则就是:

**核心思想:**类间的依赖关系应该建立在最小的接口上

**通俗来讲:**建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少。也就是说,我们要为各个类建立专用的接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。

**需注意:**接口尽量小,但是要有限度。对接口进行细化可以提高程序设计灵活性,但是如果过小,则会造成接口数量过多,使设计复杂化。所以一定要适度提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情为依赖接口的类定制服务。只暴露给调用的类它需要的方法,它不需要的方法则隐藏起来。只有专注地为一个模块提供定制服务,才能建立最小的依赖关系。

最后:学习总结——MyBtis知识脑图(纯手绘xmind文档)

学完之后,若是想验收效果如何,其实最好的方法就是可自己去总结一下。比如我就会在学习完一个东西之后自己去手绘一份xmind文件的知识梳理大纲脑图,这样也可方便后续的复习,且都是自己的理解,相信随便瞟几眼就能迅速过完整个知识,脑补回来。下方即为我手绘的MyBtis知识脑图,由于是xmind文件,不好上传,所以小编将其以图片形式导出来传在此处,细节方面不是特别清晰。但可给感兴趣的朋友提供完整的MyBtis知识脑图原件(包括上方的面试解析xmind文档)

image

除此之外,前文所提及的Alibaba珍藏版mybatis手写文档以及一本小小的MyBatis源码分析文档——《MyBatis源码分析》等等相关的学习笔记文档,也皆可分享给认可的朋友!

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

tis知识脑图原件(包括上方的面试解析xmind文档)

[外链图片转存中…(img-H1ekhTl3-1714869715521)]

除此之外,前文所提及的Alibaba珍藏版mybatis手写文档以及一本小小的MyBatis源码分析文档——《MyBatis源码分析》等等相关的学习笔记文档,也皆可分享给认可的朋友!

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

  • 15
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值