Java&JavaEE 3|学习笔记

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 快速学习Java&JavaEE 3

开发者学堂课程【高校精品课-上海交通大学 -互联网应用开发技术Java&JavaEE 3】学习笔记,与课程紧密联系,让用户快速学习知识。

课程地址:https://developer.aliyun.com/learning/course/76/detail/15752


Java&JavaEE 3


内容介绍:

一、Package Relationships: Dependency

二、Package Organization Guidelines

三、Packaging Tips: Functionally Related Classes

四、Principles of Package Design

五、What is An Architectural Pattern?

六、Java EE Platform

 

本节课主要讲包的设计原则,在Java里包是怎么回事?其实在其他语言里面也有类似的概念,上节课讲Java语言时就说了,包,实际上对应的就是看到的在文件系统里面那个目录。

那包实际上就是把代码组织起来的一种方式,那么在个工程里面,大家看到的包可以按照这个功能去划分,也可以按照实现的层次去划分管怎么说,就是把一个大的一个系统的一个模型给它变成了若干个包,然后每一个包就是一个这个系统里面的这些元素的一个

 

一、Package Relationships: Dependency

. Packages can be related to one another using a dependency relationship

包和包之间是有依赖关系,那你在描述两个朋之间的依赖关系时,就是这样。也就像我们上课,如果有同学去看过,助教做的后端,大家可以看到,

图片47.png

比如说最底下有entity这一层,这一层它实际上用一个目录来描述的,那就是一个包。这个包上面有repository这一层对它进行访问,然后再上面就是数据访问对象,再上面是服务service,再往上有这个controller,那这就是一种目录访问这个包的组织形式,这里面每一层都依赖于下一层。我们看到每一层都会依赖于它下一层去实现,所以他们之间有一个依赖关系,那样在下面例子里面我们就看到client可能会依赖于其他的比如说一些服务的提供商就像你开发一个后端程序,希望访问数据库,那么这个数据库会有一个驱动,那你的代码就需要依赖于这个数据库的驱动,这就是种依赖关系。

Client          Dependency Relationship        Supplier

Package -----------------------------------------Package

. Dependency Implications

-Changes to the Supplier package may affect the Clientpackage

-The Client package cannot be reused independently because it depends on the Supplier package

 

二、Package Organization Guidelines

. Package Functionally Cohesive Vertical and Horizontal Slices

. Package a Family of Interfaces

. Package by Work and by Clusters of Unstable Classes

. Most Responsible Are Most Stable

. Factor out Independent Types

. Use Factories to Reduce Dependency on Concrete Packages

· No Cycles in Packages

我们组织包的时候可能有几种方式,比如说一种按照垂直的分割,一种是按水平的分割。什么意思?比如说大家在开发电子书店网站的时候,可以有book,有order,这还有user这样的包,在book里面就会有对它做增删改查的这种动作的一些服务类,那么order也是一样,有产改查服务类,这个user也,这是一种分法从宏观上来分就分成了样,然后每个包里还可以再分服务层,体层,控制层等等,这是一种垂直的分格,就是按功能去分。当然也可以像我们刚才讲的,整个系统分成了实体,然后存储数据,访问对象等等,那在实体里面一个包里面就包含了所有,比如说work,有个order,当然可能还有其他的,如果大家做功能更复杂,还有其的一些类,那这是一种分法,这是一种水平分割,就是按照技术层次去做分割那不管哪一种都可以,那么你肯定是遵循了某一种原则,是怎么遵循这个原则?

图片48.png

那到这里,功能形成了一个分格,形成个B包,它的好处是每一包每一个包就给一个同学去开发,那么对业务熟悉的人喜欢用这种风格的方式,也就是说,我其实后端代码都会悉,但是对顾客的业务比较熟悉,大概这样分

图片49.png

么上图的例子就变成我对数据库访比较熟悉,这一层我来写,不管是书还是用户还是订单,我都很熟悉,我只是我只熟悉数据库访问,那这可以这样分,不管怎么分,肯定是有一定的依据,这个依据,总的来说,就是把一些功能上比较相信,或者是按照你的视角去看的水平,或者谁去看那功能上相近或者相同的这些东西分配在一个包里。那不管怎么样,一个包在给外面使用的时候,它应该暴露出一些接口,也就是说,包本身它应该就是暴露出接口,以接口和其他使用这个包的这些用户为契约的这样一种组织方式。也就是说你要用这个包,你就针对接口编程,至于这个接口里面的具体的类,可以不用去直接跟他们打交道,那比如说我们在用spring后面我们用框架用spring的时候,如果你是这个依赖注入的这种方式,就是控制完转的方式在编程,那么代码里出现的都是接口,就没有类,那么一个包,要给别人去使用的话,肯定是要有一组开发出来的接口暴露出来给别人去用。

然后我们上节课讲的依赖关系,讲到一个稳定的类和不稳定的类,那什么是稳定,越抽象越稳定,接口肯定是最稳定的,那里面带实现的肯定是不稳定。那也就是说,一个实现它可能会有一些变化,根据实际情况它会有变化,那么实现就步像接口那么稳定,应该是按照稳定的程度,同样稳定的东西,可能应该是打包在一个包里面进行组织。然后就是包和类的概念是一样,包的概念是更大力度的一个复用,我们之前学过的复用,在CR加里面叫函数,在Java里叫方法,那方法是一种复用给他力度大的一类,然后在力度大的是控件。控件就是,比如说我开发了一个用户认证的控件,它需要用户类,有认证类,然后有加密类等等,但它们合起来完成一个单独的任务,我能拿来去复用它。然后比控件更大,就是包这个概念,所以其实它只是力度不同,但总的想法就是要去复用,那所以要从复用的角度去看的话,应该包和包之间应该是吻合的,包的内部应该是吻合的,就像刚才我们谈到的,如果去访问数据库,那数据库里的驱动,它其实不是一个类,它是一组类,但这一组类合起来是在完成对数据库的一个方问,他们应该压到一起变成一个包。如果说我还有一个去管理数据库的其他功能,有这样的一些包的话,那其实它应该和驱动应该分开在两个不同的包里,比如说有一个专门去生成数据的这个做数据治理的包,比如它去管数据的质量、生命周期等等,就不应该跟驱动混在一起,那所以就是说你要把功能相对完整的东西打包在一起,但是它的职责应该相对来说单一。

然后就是在包和包之间形成依赖的时候,我们不能形成环,也就说你最后,如果把所有的开发的包拿出来,按照依赖关系画图,它应该是一个有效无环图。所以这是我们组织的代码的一种方式,那为什么要在这里花时间去讲这些东西?因为在开发后端的代码的时候,不可能说我们所有的代码都在一个目录之下,像前端的代码,也不在同一个目录下,会有component这样的目录,也会有这个view这样的目录,其中view是完整的一个页面,我们是在view之间进行跳转,然后component就是在构成view里面的各个部分,那你为什么要设计不同的目录去存储不同的你写的view或者是react的这些角度也是从代码组织的角度维护的角度去考虑的,后端代码更是这样,后端代码写的可能比前端还要复杂,还要多,那你更要按照这个包方式去组织,它所以这个合起来就是你系统的一种架构,架构如果不好,所有东西全部堆在一起,肯定很难维护。

 

三、Packaging Tips: Functionally Related Classes

. Criteria for determining if classes are functionally related

-Changes in one class' behavior and/or structure necessitate changes in another class

-Removal of one class impacts the other class

-Two objects interact with a large number of messages or have a complex intercommunication

-A boundary class can be functionally related to a particular entity class if the function of the boundary class is to present the entity class

-Two classes interact with, or are affected by changes in the same actor

-Two classes have relationships between each other

-One class creates instances of another class

按照功能去划分包,我们说这些类必须在功能上是相关的,那怎么去判断?从功能的角度去看,这些类确实相关的。上面给了一些依据,我们一个一个解释一下,我们要有一个类,它的行为或者是结构,如果要对它进行修改,那么,就必须对另外一个相关的类进行修改,否则的话,比如说大家的功能完整,在这种情况下,这两类就应该在一个包里面,它俩就是相关类。那比如说我刚才说的用户认证的这个类,你可以看到log in这个类和一个数据加密的类,那它俩就应该合在一起,因为我们的密码需要加密传输,如果没有加密传输这个类,这个log in就不能完成安全的认证,它俩的逻辑互相依赖,一方发生变化,另外一方可能就要跟着。那这种时候你就必须要做这个把它俩放到一个类里面设计

第二,不是修改了,是直接移除掉一个类,对另外的类会有影响,们另外一个类有影响的,所以要在一起这和修改本质上是一样的,就是说你无论是改了,还是把它删掉,整个功能就不完整,或者说依赖于它这些类就不完整,那们就应该放到一起。然后如果有两个对象,它俩交互的非常多,那就应该放到一个包里那么为什么交互的消息非常都要放在一个包里?我们上节课讲,其实在访问权限,我们看到的有protected,还有包访问权限,还有public,还有这个private,那就要去想俩交互非常多的话,我们上节课说要限制暴露的东西,那如果俩交互非常多,我要放到两个包里面去,俩完全不在一个包里,它只能是public才能访问到既然它们有非常频繁的交互为什么不将其放在同一个包里,用包访问权限去定义它们交互的接口,这样暴露出来的范围就是有限的,超出这个包的范围就是不能用的。那从功能内聚的角度,你也可以看到,既然它俩有大量的信息交互,那肯定它俩的功能是结耦合了,所以它们应该放到一起。

然后就是说,我们在设计的时候通常会有控制类、实体类、边界类这样的一些类的划分,那实体类就是你去封装的一些状态,这些状态当然可以是纯内存的,也可以是表示的数据库里面的一些状态,那这些东西合起来就会是表示程序里面的一种状态,那你在对它进行操作的时候就会产生一些像我们像刚才看到的服务类,或者是一些对他,如果数据库里有读写数据库的这种dao的类这些东西,那们如果在功能上类,应该放到一个大的包里面,然后可以分成小的包。也就是说单独给你个entity的包,这个类是没法单独使用的,必须要通过这样的service,再通过dao才能去访问到它,那么他们就应该存在一个大的包里,如果你要去复用它

然后我们来看,如果你的这个类在交互,但是它是给同一个actor去用的,actor就是UML,UML里面就同一键管语言里面定义就是说去使用这个相应的包,或者是类,或者是整个系统的这样的外部的一个角色,那可以是人,也可以是其他的程序,如果是同一台就同一个人,或者是同一个程序,它进行这个修改,它会影响到互相交互的两个类的话,那么它俩应该放到一起,然后它俩之间如果有组合或者是聚合这样的关系,比如说order,上课提到过一个订单里头,比如说我买的第一本书是哈利波特,买了两本,每本是十块钱,第二个订单,买了指环王三本,每一本15元,那这些就是orderitem,orderitem依附于order的,是一个一对多的关系。orderitem里面有一个外键关联到order id上,这样就是一个订单,其实可以买很多种不同的书,然后orderitem每一条在订购进入这里面的一条信息,在这个情况下,你可以看到把它俩拆开来就是不合适的,因为它俩这个组合关系,order实际上是由一组orderitem产生的,所以它们应该在一起

那如果一个类,它要创建另一个类的例,比如说数据库访问,那么我在一个数据库上要获取一个连接,那这个动作是在数据库的驱动里面去实现,那获取连接,大家看到这个东西用的是一个工厂方法这种设计模式来实现

工厂方法是什么咱们后面会讲,现在知道的一点就是我调用它就能得到一个连接,那很显然要这个方法所在的类和真正它创建出来的连接就connection这个类,应该放在一起他们都在那个驱动,为什么要这么设计?

就是因为工厂在创建这个实例,所以他要做这样的事情。那至于为什么要创建这个实例,道理很简单,就跟上节课我讲的,为什么不能在一个对象上面直接去说它的一个属性a·X等于三,而一定要是a·setX(3)这种方式去设置a的X属性,那原因是因为在setX这个函数里面,要先判断如果x小于0,直接return,否则才让X等于这个数据X的参数,也就是说我是提供了一种途径,让用户可以增加附加的逻辑在上头

那跟这道理一样,为什么我不能让用户直接去new connection()出来,因为数据库的连接是一种非常昂贵的资源,数据库的连接,应该知道它是一种非常贵的资源,安装一个数据库,比如说安装oracle这种数据库,在数据库里面就会去配置,在一个数据库上,允许连接的最大的连接数是多少?为什么要限制这个东西?就是因为连接是比较昂贵的一个资源,所以不能用户想创建就创建,我就写了这么一个get connection方法,在这里面我要判断一下现在有没有空闲的connection可用,如果有可用,就不创建新的,直接给它。总的来说,我要通过这样一种控制机制去创建这个对象,所以不能让用户直接应用,如果真有这样的机制,那么这个创建这个对象的类这个对创建对象的类,们就应该放在一个包里,这样操作才会比较方便,而且它俩的功能也是结耦合,也就脱离这个被创建的对象,你去谈创建它的那个类是没有任何意义,而脱离了创建它的工厂,直接谈这个类就不知道怎么得到的,所以这样放在一起。那么再仔细看,其实所有的话汇到一起就是一句,就是六个字,就是高内聚低耦合。也就说,如果这些类互相之间的交互非常频繁,并且他们在共同完成一个职责的任务,它就应该在一起。那么,如果它们互相之间相对独立,就该把它拆开,下面就是不应该放到一起的类型。 

.Criteria for determining when two classes should NOT beplaced in the same package

-Two classes that are related to different actors should not be placed in the same package

-An optional and a mandatory class should not be placed in the same package

与不同联系人相关的两个类不应该放在同一个包中。刚才我说,那些外部的在系统之外的东西可以是人,也可以是其他的程序,比如说开放API其他程序通过编程的方式进行交互。如果是两个不同操作的类,或者是交互的类,不应该把它们放在一个包里,比如e-book这个系统,专门开发了一个包,里面放的全是API,专门去供外部的系统通过web service来进行访问的这些用户,还有一个是GUI的库,专门去给人类用户去看,显然它俩里面的类不应该放到一起

可选类和必须按类不应该放在一起,应该就分开,那么这两句话合在一起,就是我们刚才那句高内聚低耦合的后半部分,我们是根据高内聚低耦合的原则去设计包。所以,通过这个课,希望你们能写出这个外部系统,或者能够按照模板、按最简单的方式写出这个系统的同时,考虑一下整个代码的质量。

 

四、Principles of Package Design

. Granularity: The Principles of Package Cohesion

-REP: The Release-Reuse Equivalency Principle

-CRP: The Common Reuse Principle

-CCP: The Common Closure Principle

. Stability: The Principles of Package Coupling

-ADP: The Acyclic Dependencies Principle

-SDP: The Stable Dependencies Principle

-SAP: The Stable Abstractions Principle

六个设计原则,前三个都是跟内聚有关,也就是哪些东西应该放一起,后三个是跟耦合有关的,也就是哪些东西不应该放在一起,也是包和包之间的依赖关系会是什么样

下面简单介绍这几个原则第一个是,发布重用等价原则,也就是说你在设计一个包,把它发布出去,比如说你设计了一个订单包,打算把这个包发布出去,给其他所有同学去使用。他们可以复用这个包,比如说,后台的这个助教,他做好了后台之后,他的后台就被其他的所有的同学去复用,然后其他的同学每一个做一种前台,复用这个后台。

那假设说你做的这个订单包,或者一个功能,你觉得做的非常好,别人就直接嵌入进去,去实现他的系统,那么你就要去考虑这个发布出去的包,它应该就跟重用的这个力度是一样的,什么意思?就是人家拿到你这个订单包之后,就直接嵌入到他的系统里面,就等于再重用一个代码,你不能说拿到这个订单包之后,另外还要再下载两个类,订单的功能才完整,如果这样的话,你这个包的组织就有问题,也就是他要重用的话,他会重用你整个发布出来的这个包。然后从另外一个角度去说,我发布这个包的目的是要去让别人去复用下订单的这个功能或者订单管理的功能,如果我的包里面除了订单管理之外,还有用户管理,那别人重用的时候就会产生一个问题,有一部分其他不需要,所以正常情况来说,就是你发布这个东西,别人要复用,就是会整个去拿去服用,不会说在复用的时候还要再去找其他的东西拿去服用,也不会说我想复用这个东西,发现其实我只用到你里面一点点东西,大量东西不用,如果是这样的话,你应该把你这个大的这个复用的包给他拆开,拆成可以单独去被复用的一个一个小的包,这就是所谓的发布和复用等价原则。

1.REP: The Release-Reuse Equivalency Principle

.The Release-Reuse Equivalency Principle

.The granule of reuse is the granule of release

-packages are usually the basic unit of development work and of release

-In order to provide the guarantees that reusers need, authors must

organize their software into reusable packages and then track thosepackages with release numbers

-the granule of reuse (i.e.,a package) can be no smaller than granule of release.

·Anything that we reuse must also be released and tracked.

-Either all of the classes in a package are reusable or none of them are.

.If a package contains software that should be reused, then it should not also contain software that is not designed for reuse.

第二个是公共复用原则,其实就跟刚才讲的道理类似,我要拿你东西去做复用,但是复用的原则是这个包一起被复用,如果说你这里有一部分是order,有一部分是user,那就不应该放到一起,比如说我只想order,或者只想用user,那按照这个公共复用原则会复用整包,但在复用的时候就会发现有一部分其实用不着我们知道代码在组织的时候,你不用,你引入了这个包,这包里面必须说一个user,因为你不用user,你只用order,你不用user,在自己的工程里也写了user,那代码时要特别注意要区分这两个东西要引用到对的user,所以就给带来了一个麻烦所以我们说组织这个包时就要这个包里的这些类,它的功能是结耦合的,在实现单一的原则。order和user是两个相对比较独立的,比较耦合的两个包,把它生成一个包就不合理,这就是我们说的,没有结耦合的这些类就不应该放到一个包里


2.CRP: The Common Reuse Principle

. The Common Reuse Principle

-The classes in a package are reused together.

If you reuse one of the classes in a package, you reuse them all.

. CRP helps us to decide which classes should be placed into a package

-classes that tend to be reused together belong in the same package.

. CRP tell us more about what classes shouldn't be together.

-classes which are not tightly bound to each other with class

relationships should not be in the same package.

. Guideline: Factor out Independent Types

-Organize types that can be used independently or in different contexts into separate packages.

Example

我们写一个持久化的一个包,这个持久化的包就包含了怎样访问数据库,这个外观模式。它是在说上层的应用如果要访问数据库,就要通过这个外观,所谓外观就相当于盖一堵墙,跟这墙去交互,墙后面发生事情就不用管,那它可能就会有一个or映射的机制,然后最终会调到数据库的一些相关的助手类上,比如说去获取数据库的连接、封装SQL的命令

但在这地方,大家会看到,如果数据库的dao层体(entity)层,我想去复用的话,其实没必要去复用底下,因为底下是跟数据库相关的一些东西,比如说JDBCUtilties涉及到对MySQL的操作,有MySQL的代码,而上面看到跟数据库本身没关系,在说我业务里这些实体对象什么样子,然后是通过,比如说spring、GKA,或者是类似工具帮我转换成了对数据库的操作调用底下来实现,这时候它俩逻辑相对独立,把它放到一个包已经不合适,所以把它拆开,拆成了右边的两个包,从这个角度上来说,右边的这个设计就显然比左边的这个设计要好。

图片50.png


3.CCP: The Common Closure Principle

. The Common Closure Principle

-The classes in a package should be closed together against the same kinds of changes.

. A change that affects a package affects all the classes in that packageand no other package

-maintainability is more important

. Guideline: Package by Work and by Clusters of Unstable Classes

-Reduce widespread dependency on unstable packages

-Example:

.1) there is an existing large package P1 with thirty classes, and

.2) there is a work trend that a particular subset of ten classes isregularly modified and re-released.

-refactor P1 into P1-a and P1-b, where P1-b contains the ten frequently worked on classes.

共同关闭原则,就是我放了一个包之后,要考虑一下,如果我对包里的这个类做一些修改,那么这个修改它肯定会涉及到,比如说我对A的修改肯定涉及到对依赖于A的B的修改,对B的修改,有可能会对依赖于B的C做修改等等,这样的扩散出去的话,那么是到此为止,就是这种修改的扩散能不能形成一个B包,这个B包就跟我设计的这个包是一样的,也就是说,它影响到所有的类应该在一个包里面

就是我们上面:所有的类都应该在一个包里面,它不应该说这是一个包,在另外一个包里面还有一个D类

A->B->C

D

当我修改的时候会对D这个类也要求去做一次修改,如果这样设计,显然你上面的包设计的不太对D可能就需要出现在上面一个包里,这就是所谓的公共包原则也就是说对于你的修改,应该把它局限在一个包里,但是这里面问题可能会带来一点复杂性,在于底下这里就是我们经常说我们这个依赖关系,这个包和包就依赖关系,是不稳定的去依赖稳定的,稳定的不要依赖于不稳定,也就是说这是一个不稳定的包,它应该依赖于一个稳定的包。也就是说,它这个稳定的包经常不发生修改,然后这个不稳定的包经常发生修改,但是不稳定的包依赖于稳定的包,稳定的包不依赖于不稳定的包,所以对不稳定的包做修改,不会影响到这边稳定的包。如果这个方向反了,变成这样了,稳定的包依赖于不稳定方,本来很稳定,但是因为依赖于这边不稳定的包,就会经常发生变化,那考虑到这一点,我们就会去想那什么是稳定的?

刚才说越抽象越稳定,就要依赖于这样的一个动作,所以我们看到进行上述设计组,比如说已有一个包,P1这里我们看到30个类,然后其中它的一个子集有十个类会发生一些修改,并且经常需要发布,而其他20个类,基本上不需要修改那在这种情况下就应该把这个包拆成两个包,有一个包,包含要修改的类,另外一个包,包含不需要修改的类,然后这个不稳定的类就依赖于稳定的类,这样的话就可以保证所有的类,它在做这个修改时候在这里面修成一个包,而且在做修改的时候基本上是都影响到。那么,如果说在一个包,里面有ABCDE5个类,说对A做修改会影响到B,影响到C,但会影响到D,E,在这个时候你会发现把它们放到一个包里就不太合适,这时候就把它切开一个地方包含ABC一个包包含DE,然后是ABC依赖于DE。这样的话,DE在做修改的时候就会影响到AEC,也就是说,我会把这个包里面尽量能剥离的东西全剥里,剩下东西不能剥离,是因为我任意改一个,其他的全部都会受到影响,其实所谓的公共包原则就是它包含两个方面,一方面它的影响一定发生在这个包内,不会影响这个包外的东西,再一个就是说,它应该是影响这个包里面它所有的类,那么如果有一些类永远不受影响,就应该把它剥离出来放在另外一个包。


4.ADP: The Acyclic Dependencies Principle

. The Acyclic Dependencies Principle

-Allow no cycles in the package-dependency graph.

. There are two solutions:

-Factor out the types participating in the cycle into a new smaller package.

-Break the cycle with an interface.

图片51.png

依赖关系必须是个无环的图,那就是我们现在看到的上图,在上图我们看到A类依赖于B类,B类也依赖于A类,直接导致上面的包依赖于下面的包,下面的包依赖于上面的包,这样就形成了一个环形的依赖,要解决这个问题的话,就是两边取一边,比如说把B这一边的接口独立出来放到一个包,然后A去依赖于这个接口,对着接口编程,然后B在使用A,B去实现这个接口,现在我们看到一个方向是如上图右侧所示,现在三个方向之间是没有一个环存在,因为只有不存在环的情况下,才会避免互相依赖导致的,比如说重复加载,或者说互相依赖之后,改了A就需要将B、IB都改,最后你不知道该怎么办,那这就是我们说的这个无环依赖原则,要把这种环打破变成无环,借助接口或者特别稳定的类解决环的问题。

.Example

图片52.png

这里给了一些例子,有上图第一种结构的环,然后怎么去把它拆开成上图第二种结构,通过一个新的这个包,把接口放进大家这样去依赖,打破环。还有一种就是当两个包产生依赖的时候,可以通过接口的方式来打破依赖的方向,那通过这些方法的组合,就能实现一个无环的依赖关系。


5.SDP: The Stable Dependencies Principle

. The Stable Dependencies Principle

-Depend in the direction of stability

- Depend in the direction of stability

图片53.png

接下来看到稳定依赖,稳定依赖的一个原则就是在所有的包里面,一定是不稳定的依赖于稳定的,稳定的不会去依赖于不稳定。所以它决定的是引用的这个方向问题,依赖关系应该是怎么去指向,应该是永远由不稳定的去依赖于稳定。所谓稳定和不稳定,就指的是大家抽象级别和层次,抽象级别越高他越稳定。

Example

-Violation of SDP

图片54.png

那在这里我们可以看到上面:一个稳定的包,它被有三个包依赖,但是它自身依赖了一个不稳定的包,就是非常灵活的包,它经常会发生变化因为一个稳定的包依赖了一个不稳定的包不是我们想看到的,于是解决方法就如下图。

-The cause of the bad dependency

/

-Fixing the stability violation using DIP

图片55.png

在这先分析一下为什么会产生这样一个依赖的原因因为我们稳定的包里面有一块依赖到了一个不稳定的包里的一个类,于是就把这个类提取出来一个接口,因为这个接口是可以给U用的,所以名字设为IU,然后这个稳定的包就依赖接口接口里头是不包含任何实现的是最稳定的一种东西。所以这个包可以依赖于这个接口,然后这边就实现了接口,现在会发现实现,实现不管怎么,因为我没有对实践具体编程,只是针对接口编程,所以只要接口里面暴露出来的方法不发生变化,代码就不做任何修改。


6.SAP: The Stable Abstractions Principle

. The Stable Abstractions Principle

-A package should be as abstract as it is stable

.a stable package should also be abstract

-Its stability does not prevent it from being extended

.a instable package should be concrete

-lts instability allows the concrete code within it to be easily changed

-The SAP and SDP combined amount to the DIP for packages.

.Guideline: Package a Family of Interfaces

-Place a family of functionally related interfaces in a separate package separate from implementation classes.

.The Java technologies EJB package javax.ejb is an example

稳定抽象原则,就是在告诉你什么叫稳定,怎么做才能越抽象。东西可以越稳定,一个包就应该尽可能的去抽象,那么这样的话,它就是稳定的,当然不可能所有东西全是抽象,越具体就越不稳定,也就是说,实现类是不稳定的,而接口永远是稳定,所以我们一定要把最稳定的东西暴露出去,一个包应该是暴露接口,不暴露实现类。实现类是接口的具体实现。

下面我们就会看到这样一个例子。

. Guideline: Use Factories to Reduce Dependency on Concrete Packages

-One way to increase package stability is to reduce its dependency on concrete classes in other packages

图片56.png

假设电子书店里面有一个结账功能,就会有一个结账台,假设它会去调用CreditPayment,就是用信用卡来付钱的一个类,那么信用卡在付钱的时候,最重要一个持久化的动作就是写数据库的动作,它还有一个Mapper类去赖于它去写数据库等等。在这个Mapper里面可以看到要去创建一个信用卡支付的一个对象,然后去调用这里的方法实现一次付账的动作,但是如果说结账台这边支付,一定要信用卡支付,那能不能有现金支付微信支付、支付宝支付,我为什么一定要跟信用卡绑定?跟一个更抽象的绑定好不好?

. Reduce coupling to a concrete package by using a factory object

图片57.png

那就说,可能就会有一个payment类型的一个更抽象的西,然后你的register,每次都是靠payment这个对象去进行操作,Payment底下可能就会有按照信用卡来付账的一个实现,按照支付宝来实现等等。

那如果是这样去实现的话,就会发现代码会依赖于一个接口,这个接口本身非常稳定,它底下带有若干种实现,我就对具体的实现不去做绑定,只对接口变长。未来就像刚才看到的,要求得到一个Payment类型的一个对象,至于它到底是里面的哪一个子类就不管,反正代码只对的接口变长,这样就可以使你的代码口味户性变得比较高,所以这就是稳定抽象原则,就是要让代码一定在具体的类之上封装出一层接口。

 

五、What is An Architectural Pattern?

.An architectural pattern expresses a fundamental structural organization schema for software systems.

-It provides a set of predefined subsystems, specifies their responsibilities, and includes rules and guidelines for organizing the relationships between them

-from: Buschman et al,"Pattern-Oriented Software architecture-A System of Patterns"

. Architectural patterns - Common Architectural Styles

-Layers

-Model-view-controller (M-V-C)

在知道了包的组织结构之后,再看包和包之间大的架构是什么样的。那我们讲架构的原则就是整个系统的一个高层分解,就是你把它定义成若干个子系统,或者子模块,然后把整个功能系统的功能分配到这些子模块和子功能里面去,这就是一个架构设计。架构本身也有一些模式可以去采纳的,比如说我们开发外边应用,最典型的就是分层逻辑,就刚才讲的,从entity,到repository,到dao到service到controller,这就是个分层逻辑,然后MVC模式这都是一些设计架构层面的设计模式。

我们先看分层架构:

图片58.png

分层架构,就是说,底下是持久化层,是要去定义初步如何去访问;领域层在定义系统里面的实体,它们互相之间的关系;然后有服务层,具体的业务逻辑;应用层在控制业务的流程;表示层是去给用户看,那么分层架构的一个灵魂是?上一层是依赖于下一层,它不会越过一层去依赖一层,其次,下一层不会反过来去依赖上面这一层,它永远只是上一层依赖于下一层,那这样做的一个好处是?随便挑一层,比如说service这层,application依赖于service,然后它把所有的调用全部转发给serviceservice再往下application就不管了,那是不是从service这一层来看向上就是给application提供了一个服务,然后向下全部由来接管,那就是说application眼中看到它上面只有一层就是service,至于service如何实现它不用管,那也就是说,它和domain、persistence全部解耦。

每一层都只跟它下面一层打交道,并且只为它上面一层提供相应的接口,所以未来假如domain这一层设计的不太好,想把它整个替换掉,那之前我们有说,每一层都有接口,都有实现,只要接口不动,把整个这一层的实现给替换掉,是不会对service层以及service之上的任何一层产生影响,它是不会去做修改那即使碰到一个很不好的情况,要把domain这层的接口也改掉,那它影响service层,它不会影响到application层分层架构的处就在这里

那还要强调的是,计算机软件行业里要记住一句很著名的话,就是没有什么问题是靠分层搞不定,如果加一层不行就再加一层。基本上所有软件都是这样去考虑问题,比如底下有MySQL,Oracle的数据库,一部分数据在MYSQL,一部分数据在Oracle,该怎么做?上面所有的代码在访问数据库都要访问两边,于是你觉得很不方便就在中间插了dao一层,这一层就是数据访问对象插来负责,就是说让一个数据,就MuSQLQ和Oracle数据库里面去,这样所有数据就是涉及到两个数据库,两种不同类型的数据库的交互的功能全部都集中在dao层,再往上的阶层就不用管,它眼里看到就是要去,比如说get一个M,这个M对象到底是有一部分在MySQL里,有一部分在Oracle里,还说只在MySQL里,或者在只在Oracle口里,这些细节全部靠dao封装,分层的意义就在这里所以分层每一层它的只责要单一,就在做一件事情,然后每一层要有接口和实践,这样所有代码就可以去很好维护,那么退一步再说,现在大家至少有一点体会,就是我们现在前端做成了一个单独的工程,前端在跟后端交互的时候,全部走的是HTTP的请求发送的url,其实你已经不知道后端实现,前端随便改,只要这个url是跟后来约定好了,那后台你是用spring实现,还是用struts来做这个HTTP请求的接收,还是就使用原生的servlet来处理,已经都不关心只关心两层之间的接口,就是这个url,原来大家定好就可以所以到目前为止,至少这一点能体会到,因为大家在做前端,前端的些超链接怎么写的定死,根本就不需要考虑后面到底是怎么样的实践机制,所以分层的精髓在这里,就是每一层都在向上屏蔽,从application开始往下的所有的东西,就是上方只看到一个它暴露出来的结果,再往下不用去关心。

所以说要去分层,下面是分层的例子。

图片59.png

一个是比较差的方案,一个是比较好的方案,比较好的方案,大家就看到它在分层的时候,因为我们刚才说分层,其实在Java里,比如说是以包来组织代码,那就涉及到代码是怎么去组织的那于是你就会看到你有两种方案,一种是按功能分,一种是它的技术实现的角度去分,比如service层,但是右边是逻辑视图,左边相当于把逻辑视图和实践视图混在一起至于什么叫逻辑图,什么叫实现视图,那这里我只简单说一下,就左边为什么不比右边好,大家可以看到左边把一些没有必要放到分层架构里的东西都放进了,这关于底层的具体的一些实现是从技术角度去看,到底这个数据是存储在一个数据库里,还是一个LDAP的一种数据存储结构里面,其实从架构的设计角度来说,应该通过一层去做评定,左边直接把它拉过来,放到这里就不太好,当然这里边就涉及到刚才说的到底是怎么看待这两个视图?这个细节就不展开了,但当前在组织代码时一定要遵从前面我们讲的分层结构去安排你的代码

Model-View-Controller Architecture

. The model-view-controller architecture (MVC)

-separates application data (contained in the model) from graphical presentation components (the view) and input-processing logic (the controller).

图片60.png

MVC是什么意思M就是对整个系统里的状态的封装状态是什么那有很多,比如说你看到的book,order,这是一种状态还比如说用户有没有登录他的购物车,这都是状态,就是这些状态全部被model去封装,然后会去响应这些状态的一个查询的动作,然后会把这个应用的功能,就是在实现这个业务,把应用的功能给暴露出去,那么的状态一旦发生变化,可以去通知view,但是没有去说一定是哪个view去展现的View是说只要给我一个model,我就把model里面封装的状态给它拿出来,通过query方式拿出来拿出来之后把它呈现,然后前端可能会去针对这个view上面的操作,请求更新这个model,那这个请求发行之就会发给controller controller去操作model去实现 Controller接收客户端的请求决定怎么样去更改model的状态这样分开的好处是,比如说一个model是有关book,个是前端显示的view,假设写了一个view,是用read写的,隔了一段时间觉得写不太好,想换一个用view写或者说还是用read写的,在里面做一个书的页面,但是后来想改变一下这个业务的逻辑,要直接去整展现这个书,要看这用户有没有登录过,没登录把它强制转换到另外一个页面去,就是到底view之间哪个view对应这个model不要绑死,由controller来做中间的联系,现在用这个页面来展示消息,隔了一段时间想换一个页面去展示的时候,双方的代码都不用改,就在controller里面改变一下俩绑定的逻辑就可以来实现这样的话,代码就会变得非常的灵活而这个绑定的实现是怎么实现的,在工具里面,它是通过一个X mail的文件的描述来改变的,所以它比较方便,不需要去改源码,这是MVC模式想要达到的一个效果

要展现的内容两者之间完全解耦,只管状态的变化,只管给你一个状态,你就把它呈现出来,不要管这个状态是从哪model来的,不管是哪个model,query出来它的状态之后就可以去呈现,所以俩之间要实现解耦,这样代码会变的非常的容易,所以这个架构就是我们常用的一个架构,这个道理跟前面我们讲了很多的基本原则是一样的,它要实现解耦

这里要强调:无论是分层还是MVC,它考虑是整个系统大的架构,从架构的方式这个角度上来划分系统分割,或者到一个系统的切分,就高层的一个切分,高层架构的一种方式,那这两个都属于架构设计模式,架构设计模式公认有20多种,在这个课里面至少是这两种是要应用

 

六、Java EE Platform

The Java EE application parts shown in left figure are presentedin Java EE Components.

- Client-tier components

run on the client machine.

- Web-tier components

run on the Java EE server.

- Business-tier components

-run on the JavaEE server.

- Enterprise information system

(EIS)-tier software

- runs on the EIS server.

- Ali, eBay, LinkedIn图片61.png

接下来谈一谈Java的企业版,我们的目标不是Java,而是Java企业版因为我们在开发web应用,web应用在Java企业版里,Java企业版是怎么定义?首先应该有一个Java服务应用服务器,Tomact,Jboss,高版本的是wildfly或者glassfish,这都是开源的

这个应用服务器关键的问题是应用服务器的作用是什么?我们可以想象下写了些什么,写了一些基于web页面的应用,当然也可以Java一个命令行提供应用程序,不管怎么样,它也能发出http请求访问到外端。在client一端写了各种各样的框架,在web端,比如说springservice或者Struts,有可能是原生的servlet,这些东西就是在接收客户端发送来的HTTP的请求,然后去做处理,那么应用服务器如果只是接收请求处做处理,这件事情可能听起来比较简单。实际上,比如开发一个类似电子书店的网站,需要些什么?举个例子,我们开发的东西,为什么在Java企业版这个里面,说所有的东西就是控件,这点不难理解,就像前端都是控件的,但是所有的控件在运行的时候,必须要在一个容器里去运行

Containers

-are the interface between a component and the low-level platform-specific functionality that supports the component.

-Before it can be executed, a web, enterprise bean, or application client component must be assembled into a Java EE module and deployed intoits container.

- Container settings customize the underlying support provided by the Java EE server, including such services as

. Security

. Transaction management

. Java Naming and Directory Interface (JNDI) API lookups,

. and remote connectivity.

容器,实际上就是对构建提供安全服务、事务管理,还有其他的各种各样的服务。举一个例子,你写了一个客户的应用,说前端发送过来一个请求给你的控件,控件要把用户订单写到数据库里。此时需要,第一,去获取数据库的连接,第二,在数据库获取连接时要有用户名和密码,这个东西可能来自于前端,前端传输时可能是加密的,就需要去解密,然后再去访问数据库,发SQL语句进去,然后去处理,数据库处理时,还有个事务管理,要开一个事物来指定的操作。再看,从没写一个代码去创建一个实例,然后在这个实例上去调用什么方法就能服务于它。谁来处理这个实例?客户端有很大量兴趣过来的时候,是谁来控制实的数量是一个C的实在服务于所有的客户端,还是有很多个C实例在服务于大量客户端,也就是对象的生命周期管理是谁在做那我刚才问的所有的问题,大家可以看到,其实在你的代码里,你从来没有去写过,想要一个connection,就get connection就得到了,至于它怎么得到的,怎么建立连接建立连接以后怎么能在上面进行操作?

这些东西都没有关心过,没有写过然后拿到了一个connection之后,就在上面就执行了一个SQL语句,这个SQL语句,它是怎么翻译成这个数据库能识别的格式,怎么打包以后通过网络传走同样的道理,就是这个生命周期也没人管,就说有很多东西都没写。都是Tomact写,意味着自己写的C,只是管了一下HP请求过来之后从业务的角度要做些什么操作。

为什么需要Tomact搞定,原因:tomact功能相比,自己写的业务逻辑,工作量度不大而且从技术角度上来说,自己代码的难度太小。也就说,要花很大的精力在跟业务关的工作,再从另外一个角度看,一旦写出这样的代码,怎么管理链接、怎么翻译sql语句、怎么来做生命周期管理,所以就需要借助tomact等工具。

在创建数据库连接时要知道连接到哪个数据库的哪个数据源上,食物也是,虽然能帮助管理事务api,但是要知道到哪一条操作结束是一个事务。所以,我的应用需要告诉你怎么样来帮我实现这些功能的一个东西,那于是我们在代码里看到了大量的,比如transaction一些属性,这些东西就是给tomact去看的,看了之后就知道怎么帮处理,于是tomact开始这么做了,来一个应用就生成了一个所谓的容器就是按照你定义的这些东西,你希望它帮你怎么管理这些东西,它就帮你定制一些服务,这些服务串起来就是容器,就是containers

什么叫串起来,也就是说,一个请求过来要先经过安全服务,再经过对象生命周期服务,看看要不要创建对象,再经过其他服务等等一系列的服务,最终才到写的C对象上

图片62.png

C处理完结果返回的时候,再经过查看是否需要要返回时再做些事情,以此类推,最后返回给客用客户端

图片63.png

如图框起来的一部分东西合起来就是容器,讲容器的意思就告诉大家其实服务器就是一个托管环境,它提供了很多服务,把一些跟业务无关的东西全部独立出来,帮我们完成,我们提供了很多的服务。

.Container types

图片64.png

自己写的servlet要在web容器里面去运行,它单独不能跑

刚才我举到的几个例子,数据库怎么访问的数据库怎么连接?数据怎么写进去servlet是什么等等Oracle的公司定义了一下,Tomact、JBoss这些东西都叫做外部服务器,或者JAVA EE服务器

因为Toamct根据这些接口,有一个自己的实现,Jboos也有自己的实现们不管怎么实现,它都遵循这个标准的接口,所以我们针对接口编程,所以我们写的东西能部署在tomact里,也能部署在Jboos里

所以整个Oracle就对Java企业版就定义了一大堆的规范,大家现在至少知道了一点,就是说这些规范在描述我需要使用,但我不知道该怎么写,我要去调用的这些服务做了一个标准化的动作

tomact可以去实现,Jboss也可以实现,那我只针对这些标准接口编程,所以将来的应用既可以部署在tomact里,也可以部署在Jboss里,或者是GlassFish里,这就是Java企业版的精髓

图片65.png

那它跟.Net这个平台相比,它的差异就是:.Net就是微软的一套,不存在迁移性,它有这样的一个差异,这就是Java企业。将Java企业版的目的不要大家明白每一个企业做什么,但至少要知道一点,就是我们的东西要靠容器来托管,以及这个tomact这种用服务器到底在干什么

所以我们的程序为什么叫部署不叫双击以后就执行的一个程序,原因就是因为其实你的程序只关注了业务逻辑,其他大量的功能都是tomact助完成了。

 

下面是相应的参考资料前面的面向对象的设计原则,然后包的设计原则等等,最后是Java企业版

References

JAVE SOFTWARE SOLUTIONS: FOUNDATIONS OF PROGRAM DESIGN (EighthEdition)

-JOHN LEWIS(Virginia Tech), WILLIAM LOFTUS (Accenture)

Java: An Introduction to Problem Solving & Programing(Eighth Edition)

- Walter Savitch(University of California, San Diego)

The JavaTM Tutorials -Trail: Learning the Java Language

- https://docs.oracle.com/javase/tutorial/java/index.html

SOLID

- https:// en.wikipedia.org/wiki/SOLID

Package principles

- https://en.wikipedia.org/wiki/Package_principles

Principles of Package and Component Design

-https://blog.avenuecode.com/principles-of-package-and-component-design

The Java EE 8 Tutorial - Distributed Multitiered Applications

- https://ljavaee.github.io /tutorial/overview004.html#BNAAY

相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
4月前
|
存储 Java
Java学习笔记 List集合的定义、集合的遍历、迭代器的使用
Java学习笔记 List集合的定义、集合的遍历、迭代器的使用
|
22小时前
|
存储 缓存 前端开发
JavaEE初阶——初识EE(Java诞生背景,CPU详解)
带你从零入门JAVAEE初阶,Java的发展历程认识什么是cpu,cpu的工作原理,cpu是如何进行计算的,cpu的架构,指令集,cpu的核心,如何提升cpu的算力,cpu的指令,,cup的缓存,cpu的流水线
|
1月前
|
Java 数据库连接 API
Spring 框架的介绍(Java EE 学习笔记02)
Spring是一个由Rod Johnson开发的轻量级Java SE/EE一站式开源框架,旨在解决Java EE应用中的多种问题。它采用非侵入式设计,通过IoC和AOP技术简化了Java应用的开发流程,降低了组件间的耦合度,支持事务管理和多种框架的无缝集成,极大提升了开发效率和代码质量。Spring 5引入了响应式编程等新特性,进一步增强了框架的功能性和灵活性。
49 0
|
3月前
|
存储 安全 Java
Java修仙之路,十万字吐血整理全网最完整Java学习笔记(基础篇)
从Java环境的搭建到实际代码的编写,从基本用法的讲解到底层原理的剖析,深度解析Java基础知识。本文是《Java学习路线》专栏的起始文章,旨在提供一套完整的Java学习路线,覆盖Java基础知识、数据库、SSM/SpringBoot等框架、Redis/MQ等中间件、设计模式、架构设计、性能调优、源码解读、核心面试题等全面的知识点,并在未来不断更新和完善,帮助Java从业者在更短的时间内成长为高级开发。
Java修仙之路,十万字吐血整理全网最完整Java学习笔记(基础篇)
|
3月前
|
存储 安全 Java
Java修仙之路,十万字吐血整理全网最完整Java学习笔记(进阶篇)
本文是Java基础的进阶篇,对异常、集合、泛型、Java8新特性、I/O流等知识进行深入浅出的介绍,并附有对应的代码示例,重要的地方带有对性能、底层原理、源码的剖析。适合Java初学者。
Java修仙之路,十万字吐血整理全网最完整Java学习笔记(进阶篇)
|
2月前
|
Java 数据安全/隐私保护
java学习笔记(基础习题)
java学习笔记(基础习题)
46 0
|
2月前
|
Java 程序员 开发工具
java学习笔记
java学习笔记
48 0
|
3月前
|
存储 安全 Java
Java修仙之路,十万字吐血整理全网最完整Java学习笔记(高级篇)
本文是“Java学习路线”中Java基础知识的高级篇,主要对多线程和反射进行了深入浅出的介绍,在多线程部分,详细介绍了线程的概念、生命周期、多线程的线程安全、线程通信、线程同步,并对synchronized和Lock锁;反射部分对反射的特性、功能、优缺点、适用场景等进行了介绍。
|
4月前
|
SQL druid Java
Java数据库部分(MySQL+JDBC)(二、JDBC超详细学习笔记)(下)
Java数据库部分(MySQL+JDBC)(二、JDBC超详细学习笔记)
65 3
Java数据库部分(MySQL+JDBC)(二、JDBC超详细学习笔记)(下)
|
4月前
|
SQL Java 关系型数据库
Java数据库部分(MySQL+JDBC)(二、JDBC超详细学习笔记)(上)
Java数据库部分(MySQL+JDBC)(二、JDBC超详细学习笔记)
188 3
Java数据库部分(MySQL+JDBC)(二、JDBC超详细学习笔记)(上)