java中级编程题_两道Java中级/高级开发笔试题笔试题解题思路

一、试题原题

1.1 中级试题

使用我们的脚手架工具生成项目mini-system,实现一个系统最基本的功能:用户认证+权限控制,要求:

根据RBAC模型进行数据库设计

用户密码非明文存储

使用JWT进行认证控制

使用你喜欢的框架实现接口级的权限控制

为重要的逻辑添加单元测试保护

考虑到工作量的问题,不强制要求实现数据的CRUD相关接口,你可以直接往数据库中添加数据来测试上述功能。

1.2 高级试题

这是一道设计题,为了方便理解,所有的需求均取自真实世界。

Frisk工厂可以生产许多不同类型的笔:如铅笔,钢笔和马克笔等

所有的笔可以写字,可以写到不同的载体上,如纸,玻璃

不是所有的笔都能往所有的载体上写字,如钢笔就无法把字写到玻璃上

有些载体上写的内容是可擦除的(取决于是用什么笔写的,例如马克笔写在玻璃上是可擦除的,但写在纸上是不可擦除的,而铅笔无论写在哪都可以擦除)

所有的载体可写入的内容总量都是有上限的,超过容量后再写入需要先擦除以前写的内容

每支笔都是消耗品,每次使用都会减少耐久度,不同的笔往不同的载体上写字会导致不同程度的损耗

笔的耐久度为0后将无法再使用,此时会被工厂进行回收

根据你对以上需求的理解,给出你的代码架构设计,表达形式不限(建议使用UML或伪代码),要求

架构尽可能优雅,并且不做过度设计

尽可能准确、简洁地定义每一个概念

二、中级试题解题思路

2.1 模型设计

分析:根据RBAC模型,把权限问题看成who\what\how的问题。把模型设置成用户-角色-资源-操作的模型。资源对应访问的URL路径的某一部分,而操作对应于对资源的操作,可以自定义类似于查看,修改,编辑。资源是一个逻辑概念的,资源在商务系统服务里类似商务中心-合同管理、商户中心-用户管理,而其中合同管理为商务中心的子资源,商务中心为合同管理的父资源。操作是接口级别的,接口对应一个操作,而一个接口又属于一个资源。所以用户访问接口,首先看接口对应的操作和资源。根据资源-操作去查看用户对应的角色是否有这个资源的这个操作权限,如果有则放行。

小结:

模型

graph LR

用户 -->|多对多| 角色

角色--> |多对多|资源+操作

操作:由于操作的总数量是固定的,一个操作可以用二进制某一位上的1来表示,多个操作用位或叠加

主要有如下数据库表:

表名

描述说明

用户表

包含用户信息

角色表

包含角色信息

用户角色关联表

包含用户id和角色id的关联信息

资源表

包含本资源信息和父资源id,如果为根资源,父资源id为0

操作表

包含操作码和操作描述等信息,操作码为由二进制转换成的10进制,一个二进制位对应一个操作,如果操作小于32用int类型,操作可能大于32用bigint,一般操作类型都小于10。

角色-资源-操作表

包含角色,资源,操作码(多个操作权限用位或操作得到唯一的操作码)(规则:父资源的(资源-操作)关系会覆盖子资源)

密码、JWT不做过多描述。

2.2 接口级权限控制

接口级权限控制主要是两个问题:

1.接口操作类型、资源类型如何判定?

2.根据操作类型、资源类型如何做拦截?

问题一的解决方法:资源类型使用url去判定(开发时规范接口url),操作类型开发时候使用注解标记或者url—>操作关系入库(不推荐)

问题二的解决方法:使用网关拦截(不推荐,随着系统的扩大,网关拦截的逻辑会越来越复杂),或者使用自定义注解拦截(推荐)

推荐方案 : 自定义一个权限注解,开发人员写每个接口的时候都使用这个注解标记权限(可以同时有多个操作,用位与计算),并用这个在这个自定义注解里添加拦截逻辑。

也可以使用框架,不过我对权限这一块接触较少,不知道类似的接口层权限控制框架。

2.3 单元测试保护

单元测试属于白盒测试,偏向使用PowerMockit+junit,更加高效率且低耦合(一个类的功能测试结果不依赖与其他类)。

三、高级试题解题思路

3.1 思路

看到Frisk工厂和不同类型的笔,第一想法是用抽象工厂模式:笔为一个抽象类,不同的笔为抽象类的不同实现,不同的抽象工厂的实现类实现生成不同类型的笔。

但是仔细经过审题发现如上设计存在很多问题。面试题目中所有的笔都是可以写的,都是有耐久度的,他们的区别在于写在什么类型上是可以擦除的,能写多少内容。这些都是属于属性而不是属于特性。所以把所有笔都用一个类来表示。同理,载体也用一个类的多个实例来表示

主要思路:每个笔的实例有一个typeId字段,每个载体的实例也有一个typeId字段,能不能写,能不能擦除,可以看成是一个二元关系,这个关系可以存在数据库,也可以从数据库加载缓存提高访问速度。

3.2 类与接口设计

笔的设计

每种类型的笔都是一个实例,而实例拥有内部字段typeId,这个是标记一支笔的类型,此外还有usefulLife,这个字段用来标记笔的使用寿命,每使用一次则扣除这个值。

有如下类图:

笔类图

使用DeafultPen继承Pen抽象类,并实现write()方法,参数为写的长度,实现为每写一次就扣除相应的userfulLife,扣完则不能写。

自我提问:这里为什么写方法只有writeLenth参数,没有写的内容。这里是面向对象设计,一支笔对于它本身来说不会去关心写了什么,它只关系写了多少,自己还剩下多少可以写。

载体的设计

对于载体来说,载体的属性可以有typeId和以及容量还有载体的内容content,还有position为写入的位置,这里的载体是参考java 的 Buffer来实现,初始状态position=0,每写入一个字节,则position+1,position不能超过size。如果执行clear()方法则清空载体存储区,然后清空content的内容,position置0。如下图

载体类图

write(String content,int penType)方法需要传入一个pen的实例,和需要写的内容,能不能写上或者擦除取决于Pen的typeId和Meterials的typeId。而这个关系是存在于数据库中。

其中writeHistory就是用来记录哪一段是用什么typeId的笔写的。

设计完笔和载体的模型后对这些模型进行管理:

思路是:笔(抽象类)、不同类型的笔(抽象类的实现类的不同实例)、载体(抽象类)、不同类型的载体(抽象类实现类的不同实例)。

根据题目,这里主要做笔的实例管理,即笔工厂,载体工厂和笔工厂类似。

笔工厂用来管理笔,首先要分不同类型的笔,这里用一个Map来管理,类型id为Key。同样类型的笔也有不一样的生产流水号,所以同一类型的笔会有一个集合,这个集合也用Map来管理,生产流水号为key,实例为value。

以下为笔工厂的类图,可以用init方法或者其他方法生产不同类型的笔。使用getPen()方法根据种类随机获取一支笔,也可以通过笔的唯一标识instanceId获取一支笔。这个简单工厂有Gc方法,回收那些耐久度为0的笔,可以是自动回收,也可以手动回收。

笔工厂类图

3.3 表设计(关键表)

笔/建材擦写特性表

字段

类型

说明

pen_type_id

int

笔的类型id,不同的笔对应不同的类型id

meterials_type_id

int

载体类型id,不同的载体对应不同的类型id

writable

tinyint

是否可写(1-可写,0-不可写)

erasable

tinyint

是否可擦除(1-可擦除,0-不可擦除)

...

...

...

主键:(pen_type_id,meterials_type_id)

End

本人水平有限,如有更好解题思路请留言评论或者私聊~

你可能感兴趣的:(java中级编程题)