Package Design

Package Design

Objectives

  • Organize packages to reduce the impact of changes.
  • Know alternative UML package structure notation.

Introduction

       If some package X is widely depended upon by the development team, it is undesirable for X to be very unstable (goingthrough many new versions), since it increases the impact on the team in terms of constant version re-synchronization and fixing dependent software thatbreaks in response to changes in X (version thrashing).

       This sounds and is obvious, but sometimesa team does not pay attention to identifying and stabilizing the mostdepended-upon packages, and ends up experiencing more version thrashing thannecessary, unaware of the underlying cause.

       This chapter builds on previous chapter's introduction to layers and packages, by suggesting more fine-grained heuristics for the organization of packages, to reduce these kinds of change impact. The goal is to create a robust physical package design.

       One feels the pain of fragile dependency-sensitive package organization much more quickly in C++ than in Java because of the hyper-sensitive compile and link dependencies in C++; a changein one class can have a strong transitive dependency impact leading to recompilation of many classes, and re-linking.[1]Therefore, these suggestions are especially helpful for C++ projects and moderatelyso for Java or C# (as examples) projects.

       The useful work of Robert Martin [Martin95],who has grappled with physical design and packaging of C++ applications,influenced some of the following guidelines.

SourceCode Physical Design in the Implementation Model

       This issue is anaspect of physical design the UP Implementation Model for source code packaging.

       While simply diagramming a package designon a white board or CASE tool, we can arbitrarily place types in any functionally cohesive package without impact. But during source code physical design the organization of types into physical units of release as Java or C++"packages" our choices will influence the degree of developer impact when changes in those packages occur, if there are many developers sharing acommon code base.

PackageOrganization Guidelines

Guideline:Package Functionally Cohesive Vertical and Horizontal Slices

       The basic "intuitive" principleis modularization based on functional cohesion types (classes and interfaces)are grouped together that are strongly related in terms of their participationin a common purpose, service, collaborations, policy, and function. Forexample, all the types in the NextGen Pricing package are related to product pricing. The layers and packages in the NextGendesign are organized by functional groups.

       In addition to the usually sufficientinformal guess work on grouping by function ("I think class SalesLineItem belongs in Sales")another clue to functional grouping is a cluster of types with strong internalcoupling and weaker extra-cluster coupling. For example, Register has a strong coupling to Sale, which has a strong coupling to SalesLineItem.

       Internal package coupling, or relational cohesion, can be quantified, althoughsuch formal analysis is rarely of practical necessity. For the curious, onemeasure is:


       Where NumberOfInternalRelationsincludes attribute and parameter relations, inheritance, and interfaceimplementations between types in the package.

      A package of 6 types with 12 internalrelations has RC=2. A package of 6 types with 3 intra-type relations hasRC=0.5. Higher numbers suggest more cohesion or relatedness for the package.

       Note that this measure is less applicableto packages of mostly interfaces; it is most useful for packages that containsome implementation classes.

       A very low RC value suggests either:

·        Thepackage contains unrelated things and is not factored well.

·        Thepackage contains unrelated things and the designer deliberately does not care.This is common with utility packages of disparate services (e.g., java.util), where high or low RC is not important.

·        It contains one or more subset clusters with high RC, but overall does not.

Guideline:Package a Family of Interfaces

       Place a family of functionally related interfaces in a separate package separate from implementationclasses. The Java technologies EJB package javax.ejbis an example: It is a package of at least twelve interfaces; implementationsare in separate packages.

Guideline:Package by Work and by Clusters of Unstable Classes

       The context for this discussion is that packages are usually the basic unit of development work and of release. It isless common to work on and release just one class. Unless a package is massiveor very complex, a developer is often responsible for all the types within it.

       Suppose 1) there is an existing largepackage P1 with thirty classes, and 2) there is a work trend that a particularsubset of ten classes (C1 through C10) is regularly modified and re-released.

       In this case, refactor P1 into P1-a andP1-b, where P1-b contains the ten frequently worked on classes.

       Thus, the package has been refactored into more stable and less stable subsets, or more generally, into groups related to work. That is, if most types in a package are worked on together,then it is a useful grouping.

       Ideally, fewer developers have adependency on P1-b than on P1-a, and by factoring out this unstable part to aseparate package, not as many developers are affected by new releases of P1-bas by re-releasing the larger original package P1.

       Note that this refactoring is in reactionto an emerging work trend. It is difficult to speculatively identify a goodpackage structure in very early iterations. It incrementally evolves over theelaboration iterations, and it should be a goal of the elaboration phase(because it is architecturally significant) to have the majority of the package structure stabilized by elaboration completion.

       This guideline illustrates the basicstrategy: Reduce widespread dependency on unstable packages.

Guideline:Most Responsible Are Most Stable

       If the most responsible (depended-on)packages are unstable, there is a greater chance of widespread change dependency impact. As an extreme case, if a widely used utility package such ascom.foo.util changed frequently, many thingscould break. Therefore, Figure36.1 illustrates an appropriate dependency structure.

Moreresponsible packages should be more stable.


  Visually, thelower packages in this diagram should be the most stable.

       There are different ways to increasestability in a package:

·        It contains only or mostly interfaces and abstract classes.

o    For example, java.sql contains eight interfaces and six classes,and the classes are mostly simple, stable types such as Time and Date.

·        Ithas no dependencies on other packages (it is independent), or it depends onother very stable packages, or it encapsulates its dependencies such that dependents are not affected.

o    For example, com.foo.nextgen.domain.posruleengine hides its ruleengine implementation behind a single facade object. Even if the implementation changes, dependent packages are not affected.

·        Itcontains relatively stable code because it was well-exercised and refinedbefore release.

o    For example, java.util.

·        Itis mandated to have a slow change schedule.

o    For example, java.lang, the core package in the Java libraries, issimply not allowed to change frequently.

Guideline:Factor out Independent Types

       Organize types that can be used independently or in different contexts into separate packages. Without careful consideration, grouping by common functionality may not provide the right levelof granularity in the factoring of packages.

       For example, suppose that a subsystem for persistence services has been defined in one package com.foo.service.persistence.In this package are two very general utility/helper classes JDBCUtililities and SQLCommand.If these are general utilities for working with JDBC (Java's services forrelational database access), then they can be used independently of thepersistence subsystem, for any occasion when the developer is using JDBC.Therefore, it is better to migrate these types into a separate package, such ascom.foo.util.jdbc. Figure36.2 illustrates.

Factoringout independent types.


Guideline:Use Factories to Reduce Dependency on Concrete Packages

       One way to increase package stability isto reduce its dependency on concrete classes in other packages.Figure36.3 illustrates the "before" situation.

Directcoupling to concrete package due to creation.


       Suppose that both Register and PaymentMapper(a class that maps payment objects to/from a relational database) createinstances of CreditPayment from package Payments. One mechanism to increase the long-term stabilityof the Sales and Persistencepackages is to stop explicitly creating concrete classes defined in otherpackages (CreditPayment in Payments).

       We can reduce the coupling to this concrete package by using a factory object that creates the instances, but whose create methods return objects declared in terms of interfaces rather thanclasses. SeeFigure36.4.

Reducedcoupling to a concrete package by using a factory object.


Domain ObjectFactory Pattern

The use of domain object factories with interfaces for the creation of all domain objects is a common design idiom. I have seen it mentioned informally in design literature as the Domain Object Factory pattern, but don't know of a published reference.

Guideline:No Cycles in Packages

       If a group of packages have cyclic dependency, then they may need to be treated as one larger package in terms ofa release unit. This is undesirable because releasing larger packages (orpackage aggregates) increases the likelihood of affecting something.

There are twosolutions:

1.     Factorout the types participating in the cycle into a new smaller package.

2.     Breakthe cycle with an interface.

The steps to break thecycle with an interface are:

1.

Redefine the depended-on classes in one of the packages to implement new interfaces.

2.

Define the new interfaces in a new package.

3.

Redefine the dependent types to depend on the interfaces in the new package, rather than the original classes.

Breakinga cyclic dependency.


<<Applying UML and Patterns>>

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值