首页 > 编程知识 正文

阿里js框架(阿里云售前架构师年薪)

时间:2023-05-05 22:13:11 阅读:96112 作者:4870

在大多数情况下,当我们开发应用程序时,我们不会从头开始开发它们。例如,当我们用Java开发一个网络应用程序时,我们不需要编写自己的代码来监控HTTP 80端口。不需要处理网络传输的二进制HTTP数据包;与其亲自为每个用户请求分配一个处理线程,不如直接编写一个Servlet,获取一个HttpRequest对象进行处理。我们甚至不需要从这个HttpRequest对象中获取请求参数,但是我们可以通过Controller直接获取由请求参数构造的对象。

在编写代码时,我们只需要关注自己的业务逻辑。一般的功能,比如监听HTTP端口,从HTTP请求中构造参数对象,都是由一些通用的框架来完成的,比如Tomcat或者Spring。

一、什么是框架

框架是某种架构方案的可重用设计和实现。所有的网络应用程序都需要监听HTTP端口并处理请求参数。这些功能不应该在每个Web应用程序中重复开发,而应该以通用组件的形式重用。

但并不是所有可重用的组件都被称为框架。框架通常指定软件的主要结构,它可以支持软件的整体或部分体系结构。例如,Tomcat已经完成了Web应用程序请求响应的主要过程。我们只需要开发Servlet,完成请求处理逻辑,构造响应对象,所以Tomcat是一个框架。

还有一类不控制软件主要过程或不支持软件整体架构的可重用组件。比如Log4J提供了一个可重用的日志输出函数,但是日志输出函数并不是软件的主要结构,所以我们通常称之为工具而不是框架。

一般来说,当我们使用框架编程时,我们需要根据框架规范编写代码。比如Tomcat、Spring、Mybatis、Junit等。这些框架会调用我们编写的代码,而我们编写的代码会调用工具来完成一些特定的功能,比如输出日志、匹配正则表达式等等。

我在这里强调框架和工具的区别,我不是像书一样说话。我看到一些有魄力的工程师声称自己设计开发了一个新的框架,但是这个框架并没有提供一些架构规范,也不支持软件的主体结构,只是提供了一些功能接口供开发者调用。事实上,这与我们对框架的期望相去甚远。

根据我们上面对框架的描述,当你用脚踏实地的枕头来设计一个框架的时候,其实就是在设计一类软件的通用架构,并通过代码来实现。如果只为程序调用提供功能接口,就无法支持软件的架构,也无法规范软件的结构。

那么如何设计和开发一个编程框架呢?

我前面讲过开放封闭原则。框架要满足开放封闭原则,即面对不同的应用场景,框架本身不需要修改,修改需要封闭。但是各种应用功能是可以扩展的,也就是说是开放扩展的,应用可以基于框架扩展各种业务功能。

同时,框架还应该满足依赖反转的原则,即框架不应该依赖于应用程序,因为在开发框架时应用程序并不存在。应用程序也不应该依赖框架,这样应用程序可以灵活地改变框架。框架和应用程序都应该依赖于抽象。比如Tomcat提供的编程接口就是Servlet。应用程序只需实现Servlet接口就可以在Tomcat框架下运行,并且可以随时切换到Jetty等其他Web容器,而无需依赖Tomcat。

您应该知道,虽然设计原则可以指导框架开发,但它们并没有给出具体的设计方法。事实上,该框架是通过使用各种设计模式开发的。编程框架与应用程序、设计模式和设计原则之间的关系如下图所示。

面向对象设计的目标是低耦合和高内聚。为了实现这一目标,人们提出了一些设计原则,主要包括开放封闭原则、依赖倒置原则、负责任开水置换原则、单一责任原则和接口隔离原则。在这些原则的基础上,人们总结了几种设计模式,其中最著名的是GoF23设计模式,以及Web开发学生非常熟悉的MVC模式。根据这些设计模式,人们开发了各种编程框架。使用这些编程框架,开发人员可以简单快速地开发各种应用程序。

二、Web容器中的设计模式

前面我们反复提到Tomcat是一个框架,所以

Tomcat 与其他同类的 Web 容器是用什么设计模式实现的?代码如何被 Web 容器执行?程序中的请求对象 HttpServletRequest 是从哪里来的?

Web 容器主要使用了策略模式,多个策略实现同一个策略接口。编程的时候 Tomcat 依赖策略接口,而在运行期根据不同上下文,Tomcat 装载不同的策略实现。

这里的策略接口就是 Servlet 接口,而我们开发的代码就是实现这个 Servlet 接口,处理HTTP 请求。J2EE 规范定义了 Servlet 接口,接口中主要有三个方法:

publicinterfaceServlet{ publicvoidinit(ServletConfigconfig)throwsServletException; publicvoidservice(ServletRequestreq,ServletResponseres) throwsServletException,IOException; publicvoiddestroy(); }

Web 容器 Container 在装载我们开发的 Servlet 具体类的时候,会调用这个类的 init 方法进行初始化。当有 HTTP 请求到达容器的时候,容器会对 HTTP 请求中的二进制编码进行反序列化,封装成 ServletRequest 对象,然后调用 Servlet 的 service 方法进行处理。当容器关闭的时候,会调用 destroy 方法做善后处理。

当我们开发 Web 应用的时候,只需要实现这个 Servlet 接口,开发自己的 Servlet 就可以了,容器会监听 HTTP 端口,并将收到的 HTTP 数据包封装成 ServletRequest 对象,调用我们的 Servlet 代码。代码只需要从 ServletRequest 中获取请求数据进行处理计算就可以了,处理结果可以通过 ServletResponse 返回给客户端。

这里 Tomcat 就是策略模式中的 Client 程序,Servlet 接口是策略接口。我们自己开发的具体 Servlet 类就是策略的实现。通过使用策略模式,Tomcat 这样的 Web 容器可以调用各种 Servlet 应用程序代码,而各种 Servlet 应用程序代码也可以运行在 Jetty 等其他的Web 容器里,只要这些 Web 容器都支持 Servlet 接口就可以了。

Web 容器完成了 HTTP 请求处理的主要流程,指定了 Servlet 接口规范,实现了 Web 开发的主要架构,开发者只要在这个架构下开发具体 Servlet 就可以了。因此我们可以称Tomcat、Jetty 这类 Web 容器为框架。

事实上,我们开发具体的 Servlet 应用程序的时候,并不会直接实现 Servlet 接口,而是继承 HttpServlet 类,HttpServlet 类实现了 Servlet 接口。HttpServlet 还用到了模板方法模式,所谓模板方法模式,就是在父类中用抽象方法定义计算的骨架和过程,而抽象方法的实现则留在子类中。

这里,父类是 HttpServlet,HttpServlet 通过继承 GenericServlet 实现了 Servlet 接口,并在自己的 service 方法中,针对不同的 HTTP 请求类型调用相应的方法,HttpServlet 的service 方法就是一个模板方法。

protectedvoidservice(HttpServletRequestreq,HttpServletResponseresp)th { Stringmethod=req.getMethod(); if(method.equals(METHOD_GET)){ doGet(req,resp); } elseif(method.equals(METHOD_HEAD)){ longlastModified=getLastModified(req); maybeSetLastModified(resp,lastModified); doHead(req,resp); } elseif...

由于 HTTP 请求有 get、post 等 7 种请求类型,为了便于编程,HttpServlet 提供了这 7种 HTTP 请求类型对应的执行方法 doGet、doPost 等等。service 模板方法会判断 HTTP请求类型,根据不同请求类型,执行不同方法。开发者只需要继承 HttpServlet,重写doGet、doPost 等对应的 HTTP 请求类型方法就可以了,不需要自己判断 HTTP 请求类型。Servlet 相关的类关系如下:

三、JUnit中的设计模式

JUnit 是一个 Java 单元测试框架,开发者只需要继承 JUnit 的 TestCase,开发自己的测试用例类,通过 JUnit 框架执行测试,就得到测试结果。

开发测试用例如下:

publicclassMyTestextendsTestCase{ protectedvoidsetUp(){ ... } publicvoidtestSome(){ ... } protectedvoidtearDown(){ ... } }

每个测试用例继承 TestCase,在 setUp 方法里做一些测试初始化的工作,比如装载测试数据什么的;然后编写多个以 test 为前缀的方法,这些方法就是测试用例方法;还有一个tearDown 方法,在测试结束后,进行一些收尾的工作,比如删除数据库中的测试数据等。

那么,我们写的这些测试用例如何被 JUnit 执行呢?如何保证测试用例中这几个方法的执行顺序呢?JUnit 在这里也使用了模板方法模式,测试用例的方法执行顺序被固定在 JUnit 框架的模板方法里。如下:

publicvoidrunBare()throwsThrowable{ setUp(); try{ runTest(); } finally{ tearDown(); } }

runBare 是 TestCase 基类里的方法,测试用例执行时实际上只执行 runBare 模板方法,这个方法里,先执行 setUp 方法,然后执行各种 test 前缀的方法,最后执行 tearDown方法。保证每个测试用例都进行初始化及必要的收尾。而我们的测试类只需要继承TestCase 基类,实现 setUp、tearDown 以及其他测试方法就可以了。

此外,一个软件的测试用例会有很多,你可能希望执行全部这些用例,也可能希望执行一部分用例,JUnit 提供了一个测试套件 TestSuit 管理、组织测试用例。

publicstaticTestsuite(){ TestSuitesuite=newTestSuite("all"); suite.addTest(MyTest.class); //加入一个TestCase suite.addTest(otherTestSuite); //加入一个TestSuite returnsuite; }

TestSuite 可以通过 addTest 方法将多个 TestCase 类加入一个测试套件 suite,还可以将另一个 TestSuite 加入这个测试套件。当执行这个 TestSuite 的时候,加入的测试类TestCase 会被执行,加入的其他测试套件 TestSuite 里面的测试类也会被执行,如果其他的测试套件里包含了另外一些测试套件,也都会被执行。

这也就意味着 TestSuite 是可以递归的,事实上,TestSuite 是一个树状的结构,如下:

当我们从树的根节点遍历树,就可以执行所有这些测试用例。传统上进行树的遍历需要递归编程的,而使用组合模式,无需递归也可以遍历树。

首先,TestSuite 和 TestCase 都实现了接口 Test:

publicinterfaceTest{ publicabstractvoidrun(TestResultresult); }

当我们调用 TestSuite 的 addTest 方法时,TestSuite 会将输入的对象放入一个数组:

privateVector<Test>fTests=newVector<Test>(10); publicvoidaddTest(Testtest){ fTests.add(test); }

由于 TestCase 和 TestSuite 都实现了 Test 接口,所以 addTest 的时候,既可以传入TestCase,也可以传入 TestSuite。执行 TestSuite 的 run 方法时,会取出这个数组的每个对象,分别执行他们的 run 方法:

publicvoidrun(TestResultresult){ for (Testeach:fTests){ test.run(result); } }

如果这个 test 对象是 TestCase,就执行测试;如果这个 test 对象是一个 TestSuite,那么就会继续调用这个 TestSuite 对象的 run 方法,遍历执行数组的每个 Test 的 run 方法,从而实现了树的递归遍历。

总结

人们对架构师的工作有一种常见的误解,认为架构师做架构设计就可以了,架构师不需要写代码。事实上,架构师如果只是画画架构图,写写设计文档,那么如何保证自己的架构设计能被整个开发团队遵守、落到实处?

架构师应该通过代码落实自己的架构设计,也就是通过开发编程框架,约定软件开发的规范。开发团队依照框架的接口开发程序,最终被框架调用执行。架构师不需要拿着架构图一遍一遍讲软件架构是什么,只需要基于框架写个 Demo,大家就都清楚架构是什么了,自己应该如何做了。

所以每个想成为架构师的程序员都应该学习如何开发框架。

推荐阅读

盘点:2020年最新、最全、最实用的Java岗面试真题,已收录GitHub

绝对干货,掌握这27个知识点,轻松拿下80%的技术面试(Java岗)

一线大厂为什么面试必问分布式?

在一次又一次的失败中,我总结了这份万字的《MySQL性能调优笔记》

并发编程详解:十三个工具类,十大设计模式,从理论基础到案例实战

如何高效部署分布式消息队列?这份《RabbitMQ实战》绝对可以帮到你

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。