I totally follow and propose the logical ("by-feature") organization! A package should follow the concept of a "module" as closely as possible. The functional organization may spread a module over a project, resulting in less encapsulation, and prone to changes in implementation details.
Let's take an Eclipse plugin for example: putting all the views or actions in one package would be a mess. Instead, each component of a feature should go to the feature's package, or if there are many, into subpackages (featureA.handlers, featureA.preferences etc.)
Of course, the problem lies in the hierarchical package system (which among others Java has), which makes the handling of orthogonal concerns impossible or at least very difficult - although they occur everywhere!
访问修饰符是允许其他理解您的处理的类访问您的类的内部结构的好方法。 逻辑关系并不意味着理解算法或并发约束。 功能性可能,但事实并非如此。 我对除 public 和 private 之外的访问修饰符感到非常厌倦,因为它们经常隐藏缺乏适当的架构和类抽象的情况。
在大型商业项目中,技术变化的发生频率比您想象的要高。 例如,我已经不得不更改 3 次 XML 解析器、2 次缓存技术和 2 次地理定位软件。 幸运的是,我将所有的细节隐藏在一个专用的包中......
I would personally go for functional naming. The short reason: it avoids code duplication or dependency nightmare.
Let me elaborate a bit. What happens when you are using an external jar file, with its own package tree? You are effectively importing the (compiled) code into your project, and with it a (functionally separated) package tree. Would it make sense to use the two naming conventions at the same time? No, unless that was hidden from you. And it is, if your project is small enough and has a single component. But if you have several logical units, you probably don't want to re-implement, let's say, the data file loading module. You want to share it between logical units, not have artificial dependencies between logically unrelated units, and not have to choose which unit you are going to put that particular shared tool into.
I guess this is why functional naming is the most used in projects that reach, or are meant to reach, a certain size, and logical naming is used in class naming conventions to keep track of the specific role, if any of each class in a package.
I will try to respond more precisely to each of your points on logical naming.
If you have to go fishing in old classes to modify functionalities when you have a change of plans, it's a sign of bad abstraction: you should build classes that provide a well defined functionality, definable in one short sentence. Only a few, top-level classes should assemble all these to reflect your business intelligence. This way, you will be able to reuse more code, have easier maintenance, clearer documentation and less dependency issues.
That mainly depends on the way you grok your project. Definitely, logical and functional view are orthogonal. So if you use one naming convention, you need to apply the other one to class names in order to keep some order, or fork from one naming convention to an other at some depth.
Access modifiers are a good way to allow other classes that understand your processing to access the innards of your class. Logical relationship does not mean an understanding of algorithmic or concurrency constraints. Functional may, although it does not. I am very weary of access modifiers other than public and private, because they often hide a lack of proper architecturing and class abstraction.
In big, commercial projects, changing technologies happens more often than you would believe. For instance, I have had to change 3 times already of XML parser, 2 times of caching technology, and 2 times of geolocalisation software. Good thing I had hid all the gritty details in a dedicated package...
It is an interesting experiment not to use packages at all (except for the root package.)
The question that arises then, is, when and why it makes sense to introduce packages. Presumably, the answer will be different from what you would have answered at the beginning of the project.
I presume that your question arises at all, because packages are like categories and it's sometimes hard to decide for one or the other. Sometimes tags would be more appreciate to communicate that a class is usable in many contexts.
From a purely practical standpoint, java's visibility constructs allow classes in the same package to access methods and properties with protected and default visibility, as well as the public ones. Using non-public methods from a completely different layer of the code would definitely be a big code smell. So I tend to put classes from the same layer into the same package.
I don't often use these protected or default methods elsewhere - except possibly in the unit tests for the class - but when I do, it is always from a class at the same layer
It depends. In my line of work, we sometimes split packages by functions (data access, analytics) or by asset class (credit, equities, interest rates). Just select the structure which is most convenient for your team.
From my experience, re-usability creates more problems than solving. With the latest & cheap processors and memory, I would prefer duplication of code rather than tightly integrating in order to reuse.
Advantages of separating packages like this is that it is easier to manage complexity, and it increases testability and reusability. While it seems like a lot of overhead, in my experience it actually comes very natural and everyone working on this structure (or similar) picks it up in a matter of days.
Why do I think the vertical approach is not so good?
In the layered model, several different high-level modules can use the same lower-level module. For example: you can build multiple views for the same application, multiple applications can use the same service, multiple services can use the same gateway. The trick here is that when moving through the layers, the level of functionality changes. Modules in more specific layers don't map 1-1 on modules from the more general layer, because the levels of functionality they express don't map 1-1.
When you use the vertical approach for package design, i.e. you divide by functionality first, then you force all building blocks with different levels of functionality into the same 'functionality jacket'. You might design your general modules for the more specific one. But this violates the important principle that the more general layer should not know about more specific layers. The service layer for example shouldn't be modeled after concepts from the application layer.
I find myself sticking with Uncle Bob's package design principles. In short, classes which are to be reused together and changed together (for the same reason, e.g. a dependency change or a framework change) should be put in the same package. IMO, the functional breakdown would have better chance of achieving these goals than the vertical/business-specific break-down in most applications.
For example, a horizontal slice of domain objects can be reused by different kinds of front-ends or even applications and a horizontal slice of the web front-end is likely to change together when the underlying web framework needs to be changed. On the other hand, it's easy to imagine the ripple effect of these changes across many packages if classes across different functional areas are grouped in those packages.
Obviously, not all kinds of software are the same and the vertical breakdown may make sense (in terms of achieving the goals of reusability and closeability-to-change) in certain projects.
There are usually both levels of division present. From the top, there are deployment units. These are named 'logically' (in your terms, think Eclipse features). Inside deployment unit, you have functional division of packages (think Eclipse plugins).
For example, feature is com.feature, and it consists of com.feature.client, com.feature.core and com.feature.ui plugins. Inside plugins, I have very little division to other packages, although that's not unusual too.
通常,各个部分足够大,以至于它们会被分解为单独的构建工件,您可以将核心功能放入一个 jar 中,将 api 放入另一个 jar 中,将 Web 前端内容放入一个 warfile 中,等等。
Most java projects I've worked on slice the java packages functionally first, then logically.
Usually parts are sufficiently large that they're broken up into separate build artifacts, where you might put core functionality into one jar, apis into another, web frontend stuff into a warfile, etc.
Both functional (architectural) and logical (feature) approaches to packaging have a place. Many example applications (those found in text books etc.) follow the functional approach of placing presentation, business services, data mapping, and other architectural layers into separate packages. In example applications, each package often has only a few or just one class.
This initial approach is fine since a contrived example often serves to: 1) conceptually map out the architecture of the framework being presented, 2) is done so with a single logical purpose (e.g. add/remove/update/delete pets from a clinic). The problem is that many readers take this as a standard that has no bounds.
As a "business" application expands to include more and more features, following the functional approach becomes a burden. Although I know where to look for types based on architecture layer (e.g. web controllers under a "web" or "ui" package, etc.), developing a single logical feature begins to require jumping back and forth between many packages. This is cumbersome, at the very least, but its worse than that.
Since logically related types are not packaged together, the API is overly publicized; the interaction between logically related types is forced to be 'public' so that types can import and interact with each other (the ability to minimize to default/package visibility is lost).
If I am building a framework library, by all means my packages will follow a functional/architectural packaging approach. My API consumers might even appreciate that their import statements contain intuitive package named after the architecture.
Conversely, when building a business application I will package by feature. I have no problem placing Widget, WidgetService, and WidgetController all in the same "com.myorg.widget." package and then taking advantage of default visibility (and having fewer import statements as well as inter-package dependencies).
There are, however, cross-over cases. If my WidgetService is used by many logical domains (features), I might create a "com.myorg.common.service." package. There is also a good chance that I create classes with intention to be re-usable across features and end up with packages such as "com.myorg.common.ui.helpers." and "com.myorg.common.util.". I may even end up moving all these later "common" classes in a separate project and include them in my business application as a myorg-commons.jar dependency.
Packages are to be compiled and distributed as a unit. When considering what classes belong in a package, one of the key criteria is its dependencies. What other packages (including third-party libraries) does this class depend on. A well-organized system will cluster classes with similar dependencies in a package. This limits the impact of a change in one library, since only a few well-defined packages will depend on it.
It sounds like your logical, vertical system might tend to "smear" dependencies across most packages. That is, if every feature is packaged as a vertical slice, every package will depend on every third party library that you use. Any change to a library is likely to ripple through your whole system.
I personally prefer grouping classes logically then within that include a subpackage for each functional participation.
Goals of packaging
Packages are after all about grouping things together - the idea being related classes live close to each other. If they live in the same package they can take advantage of package private to limit visibility. The problem is lumping all your view and persitance stuff into one package can lead to a lot of classes being mixed up into a single package. The next sensible thing to do is thus create view, persistance, util sub packages and refactor classes accordingly. Underfortunately protected and package private scoping does not support the concept of the current package and sub package as this would aide in enforcing such visibility rules.
I see now value in separation via functionality becase what value is there to group all the view related stuff. Things in this naming strategy become disconnected with some classes in the view whilst others are in persistance and so on.
An example of my logical packaging structure
For purposes of illustration lets name two modules - ill use the name module as a concept that groups classes under a particular branch of a pacckage tree.
apple.model apple.store
banana.model banana.store
Advantages
A client using the Banana.store.BananaStore is only exposed to the functionality we wish to make available. The hibernate version is an implementation detail which they do not need to be aware nor should they see these classes as they add clutter to storage operations.
Other Logical v Functional advantages
The further up towards the root the broader the scope becomes and things belonging to one package start to exhibit more and more dependencies on things belonging to toher modules. If one were to examine for example the "banana" module most of the dependencies would be limited to within that module. In fact most helpers under "banana" would not be referenced at all outside this package scope.
Why functionality ?
What value does one achieve by lumping things based on functionality. Most classes in such a case are independent of each other with little or no need to take advantage of package private methods or classes. Refactoring them so into their own subpackages gains little but does help reduce the clutter.
Developer changes to the system
When developers are tasked to make changes that are a bit more than trivial it seems silly that potentially they have changes that include files from all areas of the package tree. With the logical structured approach their changes are more local within the same part of the package tree which just seems right.
我目前正在进行的项目正在犯逻辑分割的错误,有一个用于 jython 方面的包,一个用于规则引擎的包,用于 foo、bar、binglewozzle 等的包。我正在考虑拥有 XML 特定的解析器/writers 该包中的每个模块,而不是有一个 XML 包(我之前已经这样做过),尽管共享逻辑仍然存在一个核心 XML 包。 然而,这样做的一个原因是它可能是可扩展的(插件),因此每个插件还需要定义其 XML(或数据库等)代码,因此集中化这可能会在以后带来问题。
所需要的是标记的命名空间。 用于某些 Jython 功能的 XML 解析器可以同时标记 Jython 和 XML,而不必选择其中之一。
或者也许我在颤抖。
It depends on the granularity of your logical processes?
If they're standalone, you often have a new project for them in source control, rather than a new package.
The project I'm on at the moment is erring towards logical splitting, there's a package for the jython aspect, a package for a rule engine, packages for foo, bar, binglewozzle, etc. I'm looking at having the XML specific parsers/writers for each module within that package, rather than having an XML package (which I have done previously), although there will still be a core XML package where shared logic goes. One reason for this however is that it may be extensible (plugins) and thus each plugin will need to also define its XML (or database, etc) code, so centralising this could introduce problems later on.
In the end it seems to be how it seems most sensible for the particular project. I think it's easy to package along the lines of the typical project layered diagram however. You'll end up with a mix of logical and functional packaging.
What's needed is tagged namespaces. An XML parser for some Jython functionality could be tagged both Jython and XML, rather than having to choose one or the other.
I try to design package structures in such a way that if I were to draw a dependency graph, it would be easy to follow and use a consistent pattern, with as few circular references as possible.
For me, this is much easier to maintain and visualize in a vertical naming system rather than horizontal. if component1.display has a reference to component2.dataaccess, that throws off more warning bells than if display.component1 has a reference to dataaccess. component2.
Of course, components shared by both go in their own package.
发布评论
评论(15)
我完全遵循并提出逻辑(“按功能”)组织! 包应尽可能遵循“模块”的概念。 职能组织可能会将模块分散到项目中,从而导致封装较少,并且容易发生实现细节的变化。
让我们以 Eclipse 插件为例:将所有视图或操作放在一个包中会很混乱。 相反,功能的每个组件都应该进入该功能的包,或者如果有很多,则进入子包(featureA.handlers、featureA.preferences 等)。
当然,问题在于分层包系统(其中 Java 有),这使得正交关注点的处理变得不可能或者至少非常困难——尽管它们无处不在!
I totally follow and propose the logical ("by-feature") organization! A package should follow the concept of a "module" as closely as possible. The functional organization may spread a module over a project, resulting in less encapsulation, and prone to changes in implementation details.
Let's take an Eclipse plugin for example: putting all the views or actions in one package would be a mess. Instead, each component of a feature should go to the feature's package, or if there are many, into subpackages (featureA.handlers, featureA.preferences etc.)
Of course, the problem lies in the hierarchical package system (which among others Java has), which makes the handling of orthogonal concerns impossible or at least very difficult - although they occur everywhere!
我个人会选择函数式命名。 简而言之:它避免了代码重复或依赖噩梦。
让我详细说明一下。 当您使用外部 jar 文件及其自己的包树时会发生什么? 您可以有效地将(已编译的)代码导入到您的项目中,并使用它生成一个(功能上分离的)包树。 同时使用这两种命名约定有意义吗? 不,除非那件事对你隐瞒了。 如果您的项目足够小并且只有一个组件,那就是这样。 但是,如果您有多个逻辑单元,您可能不想重新实现数据文件加载模块。 您希望在逻辑单元之间共享它,而不是在逻辑上不相关的单元之间存在人为依赖关系,并且不必选择要将特定的共享工具放入哪个单元。
我想这就是为什么在达到或打算达到一定规模的项目中最常使用功能命名,而在类命名约定中使用逻辑命名来跟踪特定角色(如果某个类中的每个类中存在任何角色)。包裹。
我将尝试更准确地回应您关于逻辑命名的每个观点。
如果当计划发生变化时,您必须在旧类中进行修改来修改功能,这是抽象性不好的迹象:您应该构建提供定义良好的功能的类,这些功能可以用一个简短的句子来定义。 只有少数顶级课程应该汇集所有这些内容以反映您的商业智能。 这样,您将能够重用更多代码、更容易维护、更清晰的文档和更少的依赖问题。
这主要取决于您理解项目的方式。 当然,逻辑视图和功能视图是正交的。 因此,如果您使用一种命名约定,则需要将另一种命名约定应用于类名以保持某种顺序,或者在一定深度上从一种命名约定分叉到另一种命名约定。
访问修饰符是允许其他理解您的处理的类访问您的类的内部结构的好方法。 逻辑关系并不意味着理解算法或并发约束。 功能性可能,但事实并非如此。 我对除 public 和 private 之外的访问修饰符感到非常厌倦,因为它们经常隐藏缺乏适当的架构和类抽象的情况。
在大型商业项目中,技术变化的发生频率比您想象的要高。 例如,我已经不得不更改 3 次 XML 解析器、2 次缓存技术和 2 次地理定位软件。 幸运的是,我将所有的细节隐藏在一个专用的包中......
I would personally go for functional naming. The short reason: it avoids code duplication or dependency nightmare.
Let me elaborate a bit. What happens when you are using an external jar file, with its own package tree? You are effectively importing the (compiled) code into your project, and with it a (functionally separated) package tree. Would it make sense to use the two naming conventions at the same time? No, unless that was hidden from you. And it is, if your project is small enough and has a single component. But if you have several logical units, you probably don't want to re-implement, let's say, the data file loading module. You want to share it between logical units, not have artificial dependencies between logically unrelated units, and not have to choose which unit you are going to put that particular shared tool into.
I guess this is why functional naming is the most used in projects that reach, or are meant to reach, a certain size, and logical naming is used in class naming conventions to keep track of the specific role, if any of each class in a package.
I will try to respond more precisely to each of your points on logical naming.
If you have to go fishing in old classes to modify functionalities when you have a change of plans, it's a sign of bad abstraction: you should build classes that provide a well defined functionality, definable in one short sentence. Only a few, top-level classes should assemble all these to reflect your business intelligence. This way, you will be able to reuse more code, have easier maintenance, clearer documentation and less dependency issues.
That mainly depends on the way you grok your project. Definitely, logical and functional view are orthogonal. So if you use one naming convention, you need to apply the other one to class names in order to keep some order, or fork from one naming convention to an other at some depth.
Access modifiers are a good way to allow other classes that understand your processing to access the innards of your class. Logical relationship does not mean an understanding of algorithmic or concurrency constraints. Functional may, although it does not. I am very weary of access modifiers other than public and private, because they often hide a lack of proper architecturing and class abstraction.
In big, commercial projects, changing technologies happens more often than you would believe. For instance, I have had to change 3 times already of XML parser, 2 times of caching technology, and 2 times of geolocalisation software. Good thing I had hid all the gritty details in a dedicated package...
根本不使用包(根包除外)是一个有趣的实验。
然后出现的问题是,何时以及为什么引入包是有意义的。 据推测,答案将与您在项目开始时的答案不同。
我认为您的问题根本就出现了,因为包就像类别一样,有时很难决定其中之一。 有时,标签更适合传达一个类在许多上下文中可用的信息。
It is an interesting experiment not to use packages at all (except for the root package.)
The question that arises then, is, when and why it makes sense to introduce packages. Presumably, the answer will be different from what you would have answered at the beginning of the project.
I presume that your question arises at all, because packages are like categories and it's sometimes hard to decide for one or the other. Sometimes tags would be more appreciate to communicate that a class is usable in many contexts.
从纯粹实用的角度来看,java 的可见性构造允许同一包中的类访问具有
protected
和default
可见性以及public
的方法和属性。代码>个。 使用来自完全不同的代码层的非公共方法肯定会产生很大的代码味道。 所以我倾向于将同一层的类放入同一个包中。我不经常在其他地方使用这些受保护或默认方法 - 除了可能在类的单元测试中 - 但当我这样做时,它总是来自同一层的类
From a purely practical standpoint, java's visibility constructs allow classes in the same package to access methods and properties with
protected
anddefault
visibility, as well as thepublic
ones. Using non-public methods from a completely different layer of the code would definitely be a big code smell. So I tend to put classes from the same layer into the same package.I don't often use these protected or default methods elsewhere - except possibly in the unit tests for the class - but when I do, it is always from a class at the same layer
这取决于。 在我的工作中,我们有时会按功能(数据访问、分析)或按资产类别(信贷、股票、利率)拆分包。 只需选择最适合您的团队的结构即可。
It depends. In my line of work, we sometimes split packages by functions (data access, analytics) or by asset class (credit, equities, interest rates). Just select the structure which is most convenient for your team.
根据我的经验,可重用性产生的问题多于解决的问题。 随着最新的& 便宜的处理器和内存,我更喜欢重复代码而不是紧密集成以便重用。
From my experience, re-usability creates more problems than solving. With the latest & cheap processors and memory, I would prefer duplication of code rather than tightly integrating in order to reuse.
对于包装设计,我首先按层划分,然后按其他一些功能划分。
还有一些附加规则:
因此,例如,对于 Web 应用程序,您的应用程序层中可以有以下层(从上到下):
对于生成的包布局,有一些附加规则:
这是一个示例布局。
表示层按视图技术划分,也可以选择按应用程序(组)划分。
应用层分为用例。
服务层分为多个业务域,受后端层中的域逻辑的影响。
集成层分为“技术”和访问对象。
像这样分离包的优点是更容易管理复杂性,并且提高了可测试性和可重用性。 虽然这看起来开销很大,但根据我的经验,它实际上非常自然,每个从事此结构(或类似结构)工作的人都会在几天内学会它。
为什么我认为垂直方法不太好?
在分层模型中,多个不同的高层模块可以使用同一个低层模块。 例如:可以为同一个应用程序构建多个视图,多个应用程序可以使用同一个服务,多个服务可以使用同一个网关。 这里的技巧是,当穿过各层时,功能级别会发生变化。 更具体的层中的模块不会将 1-1 映射到更一般的层中的模块上,因为它们表达的功能级别不会映射 1-1。
当您使用垂直方法进行封装设计时,即首先按功能进行划分,然后将具有不同功能级别的所有构建块强制放入相同的“功能外套”中。 您可以为更具体的模块设计通用模块。 但这违反了更一般的层不应该了解更具体的层的重要原则。 例如,服务层不应按照应用程序层的概念进行建模。
For package design, I first divide by layer, then by some other functionality.
There are some additional rules:
So, for a web application for example, you could have the following layers in your application tier (from top to bottom):
For the resulting package layout, these are some additional rules:
<prefix.company>.<appname>.<layer>
<root>.<logic>
<root>.private
Here is an example layout.
The presentation layer is divided by view technology, and optionally by (groups of) applications.
The application layer is divided into use cases.
The service layer is divided into business domains, influenced by the domain logic in a backend tier.
The integration layer is divided into 'technologies' and access objects.
Advantages of separating packages like this is that it is easier to manage complexity, and it increases testability and reusability. While it seems like a lot of overhead, in my experience it actually comes very natural and everyone working on this structure (or similar) picks it up in a matter of days.
Why do I think the vertical approach is not so good?
In the layered model, several different high-level modules can use the same lower-level module. For example: you can build multiple views for the same application, multiple applications can use the same service, multiple services can use the same gateway. The trick here is that when moving through the layers, the level of functionality changes. Modules in more specific layers don't map 1-1 on modules from the more general layer, because the levels of functionality they express don't map 1-1.
When you use the vertical approach for package design, i.e. you divide by functionality first, then you force all building blocks with different levels of functionality into the same 'functionality jacket'. You might design your general modules for the more specific one. But this violates the important principle that the more general layer should not know about more specific layers. The service layer for example shouldn't be modeled after concepts from the application layer.
我发现自己坚持鲍勃叔叔的包设计原则。 简而言之,要一起重用和一起更改(出于相同原因,例如依赖项更改或框架更改)的类应该放在同一个包中。 在我看来,在大多数应用程序中,功能细分比垂直/特定业务细分更有可能实现这些目标。
例如,域对象的水平切片可以由不同类型的前端甚至应用程序重用,并且当底层 Web 框架需要更改时,Web 前端的水平切片可能会一起更改。 另一方面,如果跨不同功能区域的类被分组在这些包中,则很容易想象这些更改在许多包中产生的连锁反应。
显然,并非所有类型的软件都是相同的,并且在某些项目中垂直细分可能是有意义的(就实现可重用性和易于更改的目标而言)。
I find myself sticking with Uncle Bob's package design principles. In short, classes which are to be reused together and changed together (for the same reason, e.g. a dependency change or a framework change) should be put in the same package. IMO, the functional breakdown would have better chance of achieving these goals than the vertical/business-specific break-down in most applications.
For example, a horizontal slice of domain objects can be reused by different kinds of front-ends or even applications and a horizontal slice of the web front-end is likely to change together when the underlying web framework needs to be changed. On the other hand, it's easy to imagine the ripple effect of these changes across many packages if classes across different functional areas are grouped in those packages.
Obviously, not all kinds of software are the same and the vertical breakdown may make sense (in terms of achieving the goals of reusability and closeability-to-change) in certain projects.
通常存在两个级别的划分。 从顶部开始,有部署单元。 这些是“逻辑上”命名的(用您的话来说,想想 Eclipse 功能)。 在部署单元内部,有包的功能划分(想想 Eclipse 插件)。
例如,feature为
com.feature
,它由com.feature.client
、com.feature.core
和com组成.feature.ui
插件。 在插件内部,我对其他包的划分很少,尽管这也很常见。更新:顺便说一句,Juergen Hoeller 在 InfoQ 上发表了关于代码组织的精彩演讲:http://www.infoq.com/presentations/code-organization-large-projects。 Juergen 是 Spring 的架构师之一,对这方面了解很多。
There are usually both levels of division present. From the top, there are deployment units. These are named 'logically' (in your terms, think Eclipse features). Inside deployment unit, you have functional division of packages (think Eclipse plugins).
For example, feature is
com.feature
, and it consists ofcom.feature.client
,com.feature.core
andcom.feature.ui
plugins. Inside plugins, I have very little division to other packages, although that's not unusual too.Update: Btw, there is great talk by Juergen Hoeller about code organization at InfoQ: http://www.infoq.com/presentations/code-organization-large-projects. Juergen is one of architects of Spring, and knows a lot about this stuff.
我参与过的大多数 java 项目首先对 java 包进行功能切片,然后再进行逻辑切片。
通常,各个部分足够大,以至于它们会被分解为单独的构建工件,您可以将核心功能放入一个 jar 中,将 api 放入另一个 jar 中,将 Web 前端内容放入一个 warfile 中,等等。
Most java projects I've worked on slice the java packages functionally first, then logically.
Usually parts are sufficiently large that they're broken up into separate build artifacts, where you might put core functionality into one jar, apis into another, web frontend stuff into a warfile, etc.
功能(架构)和逻辑(功能)包装方法都占有一席之地。 许多示例应用程序(在教科书等中找到的应用程序)遵循将表示、业务服务、数据映射和其他体系结构层放入单独包中的功能方法。 在示例应用程序中,每个包通常只有几个或只有一个类。
这种初始方法很好,因为人为的示例通常用于:1)概念性地绘制出所呈现框架的架构,2)出于单一逻辑目的(例如从诊所添加/删除/更新/删除宠物)来完成此操作。 问题是,许多读者将此视为没有界限的标准。
随着“业务”应用程序扩展到包含越来越多的功能,遵循功能方法成为一种负担。 尽管我知道在哪里寻找基于架构层的类型(例如“web”或“ui”包下的Web控制器等),但开发单个逻辑功能开始需要在许多包之间来回跳转。 这至少很麻烦,但更糟糕的是。
由于逻辑上相关的类型没有封装在一起,导致API过度宣传; 逻辑相关类型之间的交互被强制为“公共”,以便类型可以相互导入和交互(最小化默认/包可见性的能力会丢失)。
如果我正在构建一个框架库,我的包无论如何都会遵循功能/架构打包方法。 我的 API 消费者甚至可能会欣赏他们的导入语句包含以架构命名的直观包。
相反,在构建业务应用程序时,我将按功能进行打包。 我可以毫无问题地将 Widget、WidgetService 和 WidgetController 全部放在同一个“com.myorg.widget.”包中,然后利用默认可见性(并且具有更少的导入语句以及包间)依赖项)。
然而,也存在交叉案例。 如果我的 WidgetService 被许多逻辑域(功能)使用,我可能会创建一个“com.myorg.common.service。”包。 我也很有可能创建旨在跨功能重用的类,并最终得到诸如“com.myorg.common.ui.helpers”和“”之类的包。 com.myorg.common.util.”。 我什至可能最终将所有这些后来的“通用”类移到一个单独的项目中,并将它们作为 myorg-commons.jar 依赖项包含在我的业务应用程序中。
Both functional (architectural) and logical (feature) approaches to packaging have a place. Many example applications (those found in text books etc.) follow the functional approach of placing presentation, business services, data mapping, and other architectural layers into separate packages. In example applications, each package often has only a few or just one class.
This initial approach is fine since a contrived example often serves to: 1) conceptually map out the architecture of the framework being presented, 2) is done so with a single logical purpose (e.g. add/remove/update/delete pets from a clinic). The problem is that many readers take this as a standard that has no bounds.
As a "business" application expands to include more and more features, following the functional approach becomes a burden. Although I know where to look for types based on architecture layer (e.g. web controllers under a "web" or "ui" package, etc.), developing a single logical feature begins to require jumping back and forth between many packages. This is cumbersome, at the very least, but its worse than that.
Since logically related types are not packaged together, the API is overly publicized; the interaction between logically related types is forced to be 'public' so that types can import and interact with each other (the ability to minimize to default/package visibility is lost).
If I am building a framework library, by all means my packages will follow a functional/architectural packaging approach. My API consumers might even appreciate that their import statements contain intuitive package named after the architecture.
Conversely, when building a business application I will package by feature. I have no problem placing Widget, WidgetService, and WidgetController all in the same "com.myorg.widget." package and then taking advantage of default visibility (and having fewer import statements as well as inter-package dependencies).
There are, however, cross-over cases. If my WidgetService is used by many logical domains (features), I might create a "com.myorg.common.service." package. There is also a good chance that I create classes with intention to be re-usable across features and end up with packages such as "com.myorg.common.ui.helpers." and "com.myorg.common.util.". I may even end up moving all these later "common" classes in a separate project and include them in my business application as a myorg-commons.jar dependency.
包将作为一个单元进行编译和分发。 在考虑哪些类属于包时,关键标准之一是其依赖性。 该类还依赖哪些其他包(包括第三方库)。 一个组织良好的系统会将具有相似依赖关系的类聚集在一个包中。 这限制了一个库中更改的影响,因为只有少数定义良好的包会依赖于它。
听起来您的逻辑垂直系统可能倾向于“涂抹”大多数包之间的依赖关系。 也就是说,如果每个功能都打包为垂直切片,则每个包都将依赖于您使用的每个第三方库。 对库的任何更改都可能会波及整个系统。
Packages are to be compiled and distributed as a unit. When considering what classes belong in a package, one of the key criteria is its dependencies. What other packages (including third-party libraries) does this class depend on. A well-organized system will cluster classes with similar dependencies in a package. This limits the impact of a change in one library, since only a few well-defined packages will depend on it.
It sounds like your logical, vertical system might tend to "smear" dependencies across most packages. That is, if every feature is packaged as a vertical slice, every package will depend on every third party library that you use. Any change to a library is likely to ripple through your whole system.
我个人更喜欢对类进行逻辑分组,然后在其中包含每个功能参与的子包。
打包的目标
毕竟是将事物分组在一起 - 相关类彼此靠近的想法。 如果它们位于同一个包中,则可以利用包私有来限制可见性。 问题是将所有视图和持久性内容集中到一个包中可能会导致许多类混合到一个包中。 因此,下一个明智的做法是创建视图、持久性、util 子包并相应地重构类。 不幸的是,受保护的包私有作用域不支持当前包和子包的概念,因为这将有助于执行此类可见性规则。
我现在看到了通过功能进行分离的价值,因为将所有视图相关的东西分组有什么价值。 此命名策略中的事物与视图中的某些类脱节,而其他类则保持不变,等等。
我的逻辑打包结构的示例
为了便于说明,让我们命名两个模块 - 我将使用名称模块作为将类分组到包树的特定分支下的概念。
apple.model
apple.store
banana.model
banana.store
优点
使用 Banana.store.BananaStore 的客户端只能使用我们希望提供的功能。 hibernate 版本是一个实现细节,他们不需要知道,也不应该看到这些类,因为它们会给存储操作带来混乱。
其他逻辑 v 功能优势
越往根部延伸,范围就变得越广,并且属于一个包的事物开始表现出对属于其他模块的事物越来越多的依赖性。 例如,如果要检查“banana”模块,则大多数依赖项将仅限于该模块内。 事实上,“banana”下的大多数助手在这个包范围之外根本不会被引用。
为什么是功能性?
通过根据功能将事物集中在一起可以实现什么价值。 在这种情况下,大多数类都是相互独立的,很少或不需要利用包私有方法或类。 将它们重构为自己的子包虽然收效甚微,但确实有助于减少混乱。
开发人员对系统的更改
当开发人员的任务是进行一些比微不足道的更改时,他们可能会进行包含来自包树所有区域的文件的更改,这似乎很愚蠢。 通过逻辑结构化方法,它们的更改在包树的同一部分中更加本地化,这似乎是正确的。
I personally prefer grouping classes logically then within that include a subpackage for each functional participation.
Goals of packaging
Packages are after all about grouping things together - the idea being related classes live close to each other. If they live in the same package they can take advantage of package private to limit visibility. The problem is lumping all your view and persitance stuff into one package can lead to a lot of classes being mixed up into a single package. The next sensible thing to do is thus create view, persistance, util sub packages and refactor classes accordingly. Underfortunately protected and package private scoping does not support the concept of the current package and sub package as this would aide in enforcing such visibility rules.
I see now value in separation via functionality becase what value is there to group all the view related stuff. Things in this naming strategy become disconnected with some classes in the view whilst others are in persistance and so on.
An example of my logical packaging structure
For purposes of illustration lets name two modules - ill use the name module as a concept that groups classes under a particular branch of a pacckage tree.
apple.model
apple.store
banana.model
banana.store
Advantages
A client using the Banana.store.BananaStore is only exposed to the functionality we wish to make available. The hibernate version is an implementation detail which they do not need to be aware nor should they see these classes as they add clutter to storage operations.
Other Logical v Functional advantages
The further up towards the root the broader the scope becomes and things belonging to one package start to exhibit more and more dependencies on things belonging to toher modules. If one were to examine for example the "banana" module most of the dependencies would be limited to within that module. In fact most helpers under "banana" would not be referenced at all outside this package scope.
Why functionality ?
What value does one achieve by lumping things based on functionality. Most classes in such a case are independent of each other with little or no need to take advantage of package private methods or classes. Refactoring them so into their own subpackages gains little but does help reduce the clutter.
Developer changes to the system
When developers are tasked to make changes that are a bit more than trivial it seems silly that potentially they have changes that include files from all areas of the package tree. With the logical structured approach their changes are more local within the same part of the package tree which just seems right.
这取决于你的逻辑过程的粒度?
如果它们是独立的,您通常会在源代码管理中为它们创建一个新项目,而不是一个新包。
我目前正在进行的项目正在犯逻辑分割的错误,有一个用于 jython 方面的包,一个用于规则引擎的包,用于 foo、bar、binglewozzle 等的包。我正在考虑拥有 XML 特定的解析器/writers 该包中的每个模块,而不是有一个 XML 包(我之前已经这样做过),尽管共享逻辑仍然存在一个核心 XML 包。 然而,这样做的一个原因是它可能是可扩展的(插件),因此每个插件还需要定义其 XML(或数据库等)代码,因此集中化这可能会在以后带来问题。
最后,对于特定项目来说,这似乎是最明智的。 然而,我认为按照典型项目分层图的方式进行打包很容易。 您最终将得到逻辑和功能包装的混合体。
所需要的是标记的命名空间。 用于某些 Jython 功能的 XML 解析器可以同时标记 Jython 和 XML,而不必选择其中之一。
或者也许我在颤抖。
It depends on the granularity of your logical processes?
If they're standalone, you often have a new project for them in source control, rather than a new package.
The project I'm on at the moment is erring towards logical splitting, there's a package for the jython aspect, a package for a rule engine, packages for foo, bar, binglewozzle, etc. I'm looking at having the XML specific parsers/writers for each module within that package, rather than having an XML package (which I have done previously), although there will still be a core XML package where shared logic goes. One reason for this however is that it may be extensible (plugins) and thus each plugin will need to also define its XML (or database, etc) code, so centralising this could introduce problems later on.
In the end it seems to be how it seems most sensible for the particular project. I think it's easy to package along the lines of the typical project layered diagram however. You'll end up with a mix of logical and functional packaging.
What's needed is tagged namespaces. An XML parser for some Jython functionality could be tagged both Jython and XML, rather than having to choose one or the other.
Or maybe I'm wibbling.
我尝试以这样的方式设计包结构:如果我要绘制依赖图,则很容易遵循并使用一致的模式,并且循环引用尽可能少。
对我来说,在垂直命名系统中比水平命名系统更容易维护和可视化。 如果 component1.display 有对 component2.dataaccess 的引用,那么比 display.component1 有对 dataaccess 的引用会发出更多的警告。 组件2。
当然,两者共享的组件都放在各自的包中。
I try to design package structures in such a way that if I were to draw a dependency graph, it would be easy to follow and use a consistent pattern, with as few circular references as possible.
For me, this is much easier to maintain and visualize in a vertical naming system rather than horizontal. if component1.display has a reference to component2.dataaccess, that throws off more warning bells than if display.component1 has a reference to dataaccess. component2.
Of course, components shared by both go in their own package.