`
haierboos
  • 浏览: 438103 次
文章分类
社区版块
存档分类
最新评论

NOTE:Architecture and OO

阅读更多

2008-1-16
______________________________________________________________________
In this scheme(分层结构) the higher layer uses various services defined by the lower layer, but the lower layer is unaware of the higher layer.

在C/S模式刚刚兴起的时候,系统的建构多采用两层模型,前台UI和后台数据库,那时的业务逻辑多放置于UI中,或者是另一种选择,以存储过程的形式放置于数据库中,当业务逻辑变得越来越复杂的时候,人们逐渐发现这种处理方式很糟糕,当C/S模式越来越流行的时候,面向对象技术开始兴起,OO社区提出了对这一问题的一个解决方案,在UI和DB中间加入第三层:一个专门用来承担业务逻辑的分层。

对象-关系映射时,关于继承的映射方案一般有如下三种:
1.把一个类体系中的所有类映射为一个表
2.把一个类体系中的所有的具体类一一映射为一个表(抽象类或是父类中的字段映射到具体类的每一个表中),各表独立拥有主键,不利于管理记录(尤其是在参照完整性方面有很大的问题,
具体可参见JPWH中的信用卡的例子)
3.把一个类体系中的所有的类,包括抽象类(父类)一一映射为一个表,在抽象类的表中添加子类表的外键(虽然解决了主键问题,但是需表连接,有性能问题)。
三种方案的差异就是在数据冗余和访问速度之间进行权衡。具体参见PEAA:Structural Mapping Patterns

对于建立领域模型和系统数据库的先后关系:
做为系统分析的重要组成,建立领域模型是项目先期的首要任务,对于数据库的建立则根据领域模型映射出来的。

We advocate a design with more classes than tables: One row represents multiple
instances. Because database identity is implemented by primary key value,
some persistent objects won’t have their own identity. In effect, the persistence
mechanism implements pass-by-value semantics for some classes! One of the
objects represented in the row has its own identity, and others depend on that. In
the previous example, the columns in the USERS table that contain address information
are dependent on the identifier of the user, the primary key of the table.
An instance of Address is dependent on an instance of User.
这是《Java Persistence with Hibernate》中对于表格和类间映射的一个段描述。这里的讨论的核心正好是领域驱动设计中关于将实体和值对象划分为一个边界的作证,两者吻合地统一在一起了。面向对象要求细粒度,而数据库的表格则要求在不影响数据冗余的前提下建立粗粒度的表格,因为过多的表格,需要表间连接,这会造成性能问题。DDD的要求是,实体应该尽可能的简洁,它的大多数属性应该分离到值对象中。一个值对象依赖于它的一个实体。这些值对象和它们共同的实体在model中组成了一个独立的小团体,即一个边界,外总要访问这个边界里的对象,只能通过实体(聚合根)来导航得到。这有利于简化对象间的关联。而从关系数据库的映射上来说,一个边界很可能对应一张数据表,只是这个表的不同字段被映射到了这个边界内部的不同的值对像和那个包含主键的实体上了。

在建模初期如何判定一个对晚是值对像还是实体:
your first reaction
should be to make everything a value-typed class and promote it to an entity only
when absolutely necessary.

As the next step, take your domain model diagram and implement POJOs for
all entities and value types. You have to take care of three things:
■ Shared references—Write your POJO classes in a way that avoids shared references
to value type instances. For example, make sure an Address object
can be referenced by only one User. For example, make it immutable and
enforce the relationship with the Address constructor.
■ Lifecycle dependencies—As discussed, the lifecycle of a value-type instance is
bound to that of its owning entity instance. If a User object is deleted, its
Address dependent object(s) have to be deleted as well. There is no notion
or keyword for this in Java, but your application workflow and user interface
must be designed to respect and expect lifecycle dependencies. Persistence
metadata includes the cascading rules for all dependencies.
■ Identity—Entity classes need an identifier property in almost all cases. Userdefined
value-type classes (and JDK classes) don’t have an identifier property,
because instances are identified through the owning entity.
We’ll come back to class associations and lifecycle rules when we discuss more
advanced mappings later in the book. However, object identity is a subject you
have to understand at this point.

2008-3-25
______________________________________________________________________________
关于组成关系和实体、值对象之间的关系。
一般来说,组成关系暗示“部分”全完从属于“整体“,有着同“整体”一致的生命同期!这种关联关系多出现于实体和值对象之间。但是有时候也会出现在实体与实体之间(不过我认为如果是这种情况的话,要好好分析一下,看看是不是把值对象当成了实体或者说在它们之间使用聚合关系会更好些呢?),这种情况可能会另人迷惑,但是事实上我们不应该把实体与值对象的区分与对象间的关联混淆起来。从本质上说,实体与值对象的区分是由业务逻辑来定的,就实体与实体之间的组成关系,我们可以认为“部分”会与“整体”有一致地生命周期,但是对于“部分”我必须给于区分,而其他的一些对象还有可能会引用到这个部分!

IOC模式
(以下英文内容自来martin fowler的那篇论文)
1.
Expanding this into a real system, we might have dozens of such services and components. In each case we can abstract our use of these components by talking to them through an interface (and using an adapter if the component isn't designed with an interface in mind). But if we wish to deploy this system in different ways, we need to use plugins to handle the interaction with these services so we can use different implementations in different deployments.
首先,必须明白IOC是建立在基于抽象编程基础之上的,如果没有这一基础,被依赖方没
有一致的抽象接口时,进行依赖注入是没有任何意义的.因为全都是具体类,一直new就好了
.没有接口,怎么能注入所谓的"实现"呢?

2009-10-24:以上是过去的认识,现在我对IOC有了更为深入的认识.事实上,IOC并不是一定要配合基于抽象编程才有意义,IOC本质上是对复杂对象依赖管理的一种解决方案.很多时候在构造一组相互依赖的对象时,从对象职责的角度考虑时,有些对象是没有合适的对象来负责创建他们的,这时候,使用是IOC就是一个很好解决方法.而事实上这种情况是经常会出现的.尤其是在各层之间有依赖时更是如此.

从上面的描述来看,IOC可以上升到架构级别.在相邻分层之间进行依赖注入,从而扮演各层之间粘合济的作用.当然前提是下层是基于抽象编写的.

For this new breed of containers the inversion is about how they lookup a plugin implementation. In my naive example the lister looked up the finder implementation by directly instantiating it. This stops the finder from being a plugin. The approach that these containers use is to ensure that any user of a plugin follows some convention that allows a separate assembler module to inject the implementation into the lister.

"控制反转"这个名词并不能准确地描述它所代表的东西.如果强行解释的话就是;原来
当依赖方需要使用被依赖方的时候往往是自行实例并持有被依赖方,而IOC则通过提供一个IOC容器来统一管理和控制依赖方和被依赖方之间的依赖关系.从某种意义上说这种
对依赖的控制权发生了转移.但是实际上这个说法并怎么贴切.还是依赖注入更能说明问题的本质.

2009-10-16

谁来创建我

当我们分析清楚客户需求设计出用例模型以后,当我们分析清楚客户的业务环境制作出领域模型以后,当我们综合用例模型、领域模型和我们的聪明才智设计出一个又一个的类和它们各自的方法以后,当就在一切都准备就绪只欠东风的关键时刻,一个对象发出了撕心裂肺的怒吼——谁来创建我?!!!一个对象,不管拥有多么强大的功能,不管进行了多么精巧的设计,如果不能被创建,就如同韩信不能做将军,孙膑不能当军师,勾践不能回越国,刘备不能得荆州,一切一切的雄才武略都如废纸一张。既然创建对于对象如此重要,我们就来好好探讨一下GRASP中关于对象创建的问题。

创建对象是面向对象系统中常见的活动之一,然而创建往往伴随着耦合。我们应当追求低耦合,但是又必须要创建对象。怎么办呢?最佳的办法就是让那些必须与创建的对象耦合的类,去完成创建的工作。创建者模式为我们提出了以下建议:

如果要将创建A的职责分配给B,那么应当满足以下条件(越多越好):

lB包含或聚合A

lB记录A

lB调用A

lB拥有A的初始化数据,并且在创建A的时候要传递给A,即BA的专家。

如果有多个类满足以上条件,首选满足条件12的类。从以上条件可以看出,即使B没有创建AB已经与A耦合了,所以B创建了A,也不会提高系统的耦合度,因此是最佳的选择。

但是,在软件系统中还有许多的例外情况不按照创建者模式创建对象。当创建一个对象,或者与它相关的整个聚合,其业务逻辑变得非常复杂时,为了不过多地暴露其内部结构,保证系统的封装性,可能选择工厂模式创建对象。在这种情况下,可能涉及到创建对象的复杂组装过程(这个问题还会在后面继续讨论)。另一种情况是,系统中的服务类对象,为了提高系统运行效率而采用单例的方式,为其它对象提供服务。像这样的对象类,我们往往采用,在系统启动,或第一次有对象访问它们时,由系统对其进行创建,例如spring框架中的那些bean

回想一下DiamondFlying游戏的领域模型里,ArticleMatrix和Article,以及Article和ChainRecation都是前者直接创建后者的,而GameRunningManager和ArticleMatrix以及controller和view都是由spring的IOC容器来创建维护的,不同的决策从本质上来看还是从这些对象的职责和它们之间的相互关系上来考量的.

2009-10-17

关于UML图绘制意图,粒度和维护,Applying UML and Patterns一书对绘制UML图的目的有一段非常务实的描述:

During UML drawing, we adopt the realistic attitude (also promoted in agile modeling) that we are drawing the models primarily to understand and communicate, not to document. Of course, we expect some of the UML diagrams to be useful input to the definition (or automated code generation with a UML tool) of the code.

我们绘制UML是为了理解和阐明领域模型,以及交流.并不是为了做文档.对UML绘制力度,我看我们必须采取务实的态度.不可过于笼统亦不能细致到如最终代码导出的那般.这个实际的操控问题!

一般来说:在UML的使用力度上,有三种方式:

2009-10-23

关于OOAD一个简单过程的描述(适用于小型项目):

  • UML as sketch Informal and incomplete diagrams (often hand sketched on whiteboards) created to explore difficult parts of the problem or solution space, exploiting the power of visual languages.

  • UML as blueprint Relatively detailed design diagrams used either for 1) reverse engineering to visualize and better understanding existing code in UML diagrams, or for 2) code generation (forward engineering).

  • UML as programming language Complete executable specification of a software system in UML. Executable code will be automatically generated, but is not normally seen or modified by developers; one works only in the UML "programming language." This use of UML requires a practical way to diagram all behavior or logic (probably using interaction or state diagrams), and is still under development in terms of theory, tool robustness and usability.

  • 2009-11-7

    设计上的概念抽象技巧:

    OO设计时如何抽象出重要且准确的业务概念是非常重要的一门技术!对设计者来说:即不能遗漏一些重要的概念,同时也不能过度设计,创造一些不和适的对象出来.如何能忠实的反映业务模型是需要好好推敲的!

    以设计BBS时,Thread的Message为例:对于一个Thread来说,它有一个在Thread创建时就生成的一条Message,我们先称其为Subject,然后会人回贴,我们称之为Reply,对于这两种事物我们如何建模呢?起初我倾向于创建一个Message抽象类,然后设计Subject和Reply来继承Message.但是从实际情况来看,Subject几乎与Reply的结构和行为完全一致,唯一不同的是Reply可以有一个引用对象是一个Message.这种情况下我审视自己的设计是否有过度设计的嫌疑.最终我认为我对Subject和Reply这两个概念的抽象有问题,严格来说,它们是同一种事物,之所以有两个不同的叫法是因为它们之于Thread的地位不一样,或者说是他们扮演了不同的角色(实质上,我范了OO中经常范的一错误:就是把同一事物的不同实例或是表现当成了另一种事物.).这是认识领域模型过程中一个重要的突破.

    由此可以看出,在领域建模时,一定要看清楚领域中各种概念的本质,准确拿捏概念!这样才能建立一个准确合理并能真实反映领域问题的模型!

    2009-11-10

    ORM能够完全阻断数据库关系模型对领域模型的影响么?

    来看一个实例:在设计论坛系统时,领域模型:Forum包含一组Thread,这是一个很直白的组成关系,从业务模型来看,这也是天经地义的.于是Forum类中有一个List<Thread> getThreads()方法也是理所当然的了.从数据库的角度来看:Thread表有一个指向Forum的外键关联也是正确的.但是当数据库中一个Forum拥有大量的Thread时,我们会发现getThreads这个方法是很恐怖的方法.但是从领域模式看来,getThreads确实是一个合理的设计,于是问题来了,到底是那是做的不对,这个getThreads方法看起来如此别扭?

    领域模型只是描述领域问题的,当我们把领域模型转化为类模型的时候,似乎有一个隐含的前提或是背景被大家忽略了,那就是所有对象的实例都是存在于内存里的!

    对象存放于哪里并不领域模型所要关心和描述的问题,这已经是程序实现模型(就是类模型吧)所要负责的事情了。

    那好,我们来看看我们要怎样做才能让我们的类模型能够即体现业务模型定义的业务含义,同时又能保证在具体的实现层面顾及数据量的问题.很显然,我们需要这样一种东西,它能够代表全体Thread的集合,但是它不一定要立即把集合的全体加载出来,它可以有选择的一部分一部分地加载某些数据.这是个东西就是一个迭代器!这个设计来自于yazd论坛的源码,确实是很巧妙的一个设计.

    对于迭代器这样的东西带有明显的程序设计色彩,在领域模型里是不会出现这种东西的.只有在设计和实现模型里,才会出现.

    2009-11-13

    继续上面的话题.我们现在从对象职责的角度来考虑这个问题.我们说Forum包含一组Thread的集合是很合理的.另外工或许也有一个createThread的方法用来创建一个新的Thread的实例,这也是合理的,因为这也是它的一个可能的"责任".但是对于已有Thread的,它们不是一直存在于内存里的.把它们从数据库中"重建"出来的责任由Forum来担当是不合适的.Forum"天生"就有那些已有的"Thread"了.它们没有责任来"重建"它们.这个责任应该由更合适的对象来承担.并且这个"重建"的操作可能要涉及数据访问相关的细节!

    DAO VS Repository

    DAO在以往的Transaction Script型的系统架构中,基本是做为一个单独数据访问层专门为Service层提供数据访问服务的.而在领域模型里,Repository做为领域模型的一个组成部分,其职责被提升为:负责领域对象的"重建"工作.这其中必然会涉及数据访问方面的工作,但是Repository相对于DAO已经有质的区别了.在领域驱动设计的系统里,模型层以下不再需要独立的数据访问层来支持了,这些已经封装到Repository中去了.而Repository是则是模型中一类重要的对象,其角色可以和Factory来做对比.

    Repository所担当的职责正是领域模型中所有领域对象都不会也不能承担的一个责任:它们如何被"重建".领域模型要工作,就需要Repository的支持,来"重建"出需要的对象,进而通过对象间的相互协作完成业务逻辑的运算!

    以下是MF在其PoEAA一书中所举的实例:

    Example: Finding a Person's Dependents (Java)

    From the client object's perspective, using a Repository is simple. To retrieve its dependents from the database a person object creates a criteria object representing the search criteria to be matched and sends it to the appropriate Repository.

    public class Person { 
       public List dependents() {
          Repository repository = Registry.personRepository();
          Criteria criteria = new Criteria();
          criteria.equal(Person.BENEFACTOR, this);
          return repository.matching(criteria);
       }
    }
    

    Common queries can be accommodated with specialized subclasses of Repository. In the previous example we might make a PersonRepository subclass of Repository and move the creation of the search criteria into the Repository itself.

    public class PersonRepository extends Repository { 
       public List dependentsOf(aPerson) {
          Criteria criteria = new Criteria();
          criteria.equal(Person.BENEFACTOR, aPerson);
          return matching(criteria);
       }
    }
    

    The person object then calls the dependents() method directly on its Repository.

    public class Person { 
       public List dependents() {
          return Registry.personRepository().dependentsOf(this);
       }
    }
    

    这个例子告诉我们了两个非常重要的点:

    1.Repository出现在了领域对象的业务方法中!这是一个强有力的信号,表明了Repository和DAO的本质区别,Repository不是一种提供数据访问的下层服务,它是模型层里的一种重要的产物(Artifact).它的职责正同它的名字:一个存放着某种"对象"全体集合的仓库!从客户端的角度来看,它就是一个巨大的List.

    2.Repository也提供对其管理对象的CRUD操作.但是这些方法都有一个很鲜明的特点:它们的参数和返回值都领域对象或是与领域对象有关的对象,绝不会出现SQL一类的东西.这也说明:Repository是模型层中的一类角色!!

    2009-11-16

    从大处来看:Repository是面向领域模型的数据访问机制.从客户端的角度看,它像是一个存储了所有已知对象的集合.但其内部却封装了和数据库交互的逻辑.而DAO则是在Transaction Script类型的系统中,面向服务层提供的针对单个实体的数据访问机制.

    从某种角度上说:Repository要比DAO具有更高的抽象级别.更倾于领域模型.粒度也更大(针对实体根设计).而DAO针对实体设计,粒度更细,更倾向于数据库.

    2009-11-20

    关于DTO:
    使用OO技术构建的领域模型都是细粒度的对象,执行一个来自Client的请求时需要要调用多个领域对象相互协作并返回它们的数据,这种情况是很常见的.如果是本地调用不会有什么问题,但是对于分布式系统的远程调用,性能开销就成为一个问题.于是人们设计出面向Client请求的,粗粒度的,仅作为数据载体的DTO,用来收集多个领域对象的数据(这个收集过程是在被请求端的本地完成的),一次性传回Client,从而减少了系统开销!另外DTO并不一定是领域对象的镜像,它应该是面向Client请求的.因此,从某种角度上看,DTO还有这样一个作用:即,当领域对象发生改变时,展现层可以不受影响.(但是组装DTO的逻辑要改动)
    虽然DTO是在分布式系统架构下提出的,但是它对于所有领域驱动的系统具有普遍意义.这个普遍意义就是:由于DTO的存在,可以避免领域对象扩散至领域模型以外的地方.这对于构建一个独立的自主控制的领域模型有很重要的意义.当然,在构建领域模型时,DTO并非是一个必选项.引入DTO也会增加系统的复杂性和更多的内存开销.这也是需要架构师进行权衡的地方.特别的,如果领域对象被某种机制隐式地管理,开发人员要特别小心这些对象扩散至领域模型以外的层面时可能出现的问题,像Hiberante中经常出现的"LazyInitializationException: Session has been closed"就属于这类情况.这些问题也从反面印证了将领域对象及其所依赖底层机制封装并限定在领域模型中的必要性.

    2009-

    2009-

    2009-

    2009-

    2009-

    2009-

    分享到:
    评论

    相关推荐

    Global site tag (gtag.js) - Google Analytics