具有相同名称但名称空间不同的多个类?


11

我遇到了一些代码(如果有问题,请使用C#),这些代码具有相同的名称,但名称空间不同。它们都倾向于代表相同的逻辑事物,但往往是同一对象的不同“视图”。有时,不同的名称空间是同一解决方案甚至同一dll的一部分。

我有我的想法,但是找不到关于此实践的任何明确定义,可以认为是对可用工具的明智使用或避免的模式。

关于蓝精灵命名的问题触及了这一点,但是是从另一个方向出发的。消除名称歧义可能需要一个任意且普遍的前缀,该问题希望消除。

有关此做法的指导原则是什么?


1
在OOP中,有很多例外,但是起初我认为这是一个糟糕的设计,特别是如果所有类都共享相同级别的可访问性。当我在任何文件中读取类名称时,那么我想知道该类立即位于哪个名称空间中,而不必引用名称空间指令。
Leopold Asperger

存在使多个相关对象处理相同状态的有效情况,但是通常它们由功能分开。也许您有一个可以为视图修改模型的对象。另一个可能会对对象执行计算。另一个可能是适配器或外观。所以,你可能有FooAdapterFooViewerFooCalculator,等,但肯定不是叫同样的事情。但是,如果没有关于您正在谈论的特定类的更多信息,则很难提供答案。

@JohnGaughan-考虑Employee作为示例。有一个雇员是其他雇员的雇员,一个是人力资源雇员,一个是外部雇员。他们都被称为“雇员”,并且都有各种各样的助手(EmployeeRepository,EmployeeView等)。它们都在同一个dll中,并且虽然它们共享一些公共属性(名称,标题),但它们都不同(电话号码,薪水)。
Telastyn 2014年

使用Employee过多次建模的系统后,听起来好像您需要一个具有所有属性并包含一个typedepartment属性的模型。否则,将不必要地重复代码。

5
这不是命名空间开始的原因之一吗?要允许名称冲突?
Dan Pichelman 2014年

Answers:


5

在我也曾在我的一个项目中看到并进行过处理之后,我将尝试针对此类用法给出答案。

代码可读性

首先,考虑到代码的可读性很重要,这种做法与之相反。有人读了一段代码,我们只说它有一个功能doSomething(Employee e)。这不再可读,因为由于Employee不同程序包中存在10个不同的类,您将必须首先借助import声明来查找输入的真正含义。

但是,这是一个高级视图,我们似乎经常会出现随机的名称冲突,没人在乎甚至找不到,因为其含义可以从其余代码以及您所处的程序包中得出。所以甚至可以争论在本地,这没有问题,因为当然,如果您Employeehr软件包中看到,则必须知道我们正在谈论员工的HR视图。

但是,一旦离开这些程序包,事情就会崩溃。一旦您在不同的模块/程序包/等中工作并且需要与员工一起工作,如果您不能完全限定其类型,则已经在牺牲可读性。此外,拥有10个不同的Employee类别意味着您的IDE的自动完成功能将不再起作用,您必须手动选择员工类型。

代码重复

由于这些类彼此之间的本质相关性,由于大量重复,您的代码势必会恶化。在大多数情况下,每个类都必须具有员工姓名或标识号之类的东西。即使每个类都添加了自己的视图,但如果它们不共享底层的员工数据,那么您最终将获得大量无用但昂贵的代码。

代码复杂度

您可能会问什么如此复杂?毕竟,每个类都可以使它尽可能简单。真正成为问题的是如何传播更改。在功能相当合理的软件中,您可能可以更改员工数据-并且您希望将其反映到任何地方。假设一位女士刚刚结婚,您必须将她从改名XY因为那个。在所有地方都很难做到这一点,但是当您拥有所有这些不同的类时,就更难了。根据您的其他设计选择,这很可能意味着每个类都必须实现自己的侦听器或将其通知更改的类-基本上,这将转化为要处理的类数的一个因素。 。当然,更多的代码重复,以及更少的代码可读性,..诸如此类的因素在复杂性分析中可能会被忽略,但是当应用于代码库的大小时,它们确实很烦人。

代码通讯

除了上述与代码选择有关的代码复杂性问题之外,您还降低了高级设计的清晰度,并失去了与领域专家进行正确沟通的能力。在讨论体系结构,设计或需求时,您将不再有空发表类似的简单声明given an employee, you can do ...。开发人员将不再知道此时的employee真正含义。虽然领域专家当然会知道。大家都这样做。有点。但是就软件而言,通信变得不那么容易了。

如何摆脱这个问题

在意识到这一点之后,如果您的团队同意这是一个足以解决的大问题,那么您将必须找到一种解决之道。通常,您不能要求经理让整个团队休假一周,以便他们清除垃圾。因此,从本质上讲,您将必须找到一种可以一次消除一部分此类的方法。最重要的部分是与整个团队一起确定Employee真正的含义。难道不是所有的个人属性整合到一个神的员工。更多地考虑核心员工,最重要的是,决定该Employee班级将驻留在哪里。

如果您进行代码审查,那么特别容易确保问题至少不会进一步加剧,即,当每个人都想Employee再次添加另一个时,将他们停在正轨上。还请注意,新子系统必须遵守协议Employee,并且不允许访问旧版本。

根据您的编程语言,您可能还想标记一些最终将被淘汰的类,例如@Deprecated帮助您的团队立即意识到他们正在使用需要更改的内容。

至于摆脱过时的员工类别,您可以为每个案例决定如何最好地消除它,或者只是就一个通用模式达成共识。您可以添加类名并将其包装在真正的雇员周围,还可以使用模式(想到的装饰器或适配器)或or或or。

长话短说:从技术上讲,这种“做法”是合理的,但是却充满了隐性成本,这些隐性成本只会在以后发生。虽然您可能无法立即解决该问题,但可以立即开始处理其有害影响。


拥有Core类似乎是一个很好的解决方案。其他类可以从中扩展。关于IDE,自动完成功能足够智能,可以知道向您建议哪个类以最佳地匹配上下文。总的来说,我不明白为什么这是一个很大的问题,尤其是在内部使用代码库的情况下。
知情的2014年

1
一旦您处于一个上下文之外,这还不够聪明。考虑一个类,它实际上需要两个有问题的类-自动完成根本没有帮助。而且它甚至不会将它们与其他十个类别区分开。但是真正的问题是,新的团队成员甚至不知道所有这些软件包域实际上是什么,以及他应该选择哪个。
2014年

如果在同一个上下文中使用多个具有相同名称的类,则很难。我还没有看到这种情况。我已经看到了多个具有相同名称的类,但是您经常不在同一个上下文中使用它们。
通知2014年

@randomA-可悲的是,这种情况在此代码库中相当普遍。
Telastyn 2014年

很好,但是您并没有真正解决核心问题,只有解决核心问题后才使用方法(“员工真正是什么”)。您舍弃的唯一显而易见的“解决方案”(“将所有各个属性集成到一个上帝雇员中”)是正确的。假设您有DB.Employee,Marshalling.Employee,ViewModels.Employee,GUI.Views.Employee等,那么您的解决方案是什么?
Pablo H

9

Scala收集框架就是一个很好的例子。有可变的和不可变的集合,以及串行和并行(将来可能还会分发)的集合。因此,例如,有四个Map特征:

scala.collection.Map
scala.collection.immutable.Map
scala.collection.mutable.Map
scala.collection.concurrent.Map

加3 ParMaps:

scala.collecion.parallel.ParMap
scala.collecion.parallel.immutable.ParMap
scala.collecion.parallel.mutable.ParMap

更有趣的是:

scala.collection.immutable.Map            extends scala.collection.Map
scala.collection.mutable.Map              extends scala.collection.Map
scala.collection.concurrent.Map           extends scala.collection.mutable.Map

scala.collecion.parallel.ParMap           extends scala.collection.Map
scala.collecion.parallel.immutable.ParMap extends scala.collecion.parallel.ParMap
scala.collecion.parallel.mutable.ParMap   extends scala.collecion.parallel.ParMap

因此,ParMap延伸ParMap延伸MapMap延伸Map延伸Map

但是,让我们面对现实吧:还有什么你给他们打电话?这是很合理的。这就是名称空间的用途!


3
“这就是名称空间的目的!” => +1
svidgen 2014年

我喜欢这样,但是在C#/。NET世界中,当我将并行类移到Parallel子命名空间中时,便与辅助类System.Threading.Parallel发生冲突,而我恰好在大量使用该类来获取并行性。我不想使用名称空间“ Concurrent”来代替,因为该名称空间已经被用来表示对集合类的“并发访问”。作为一种折衷,我选择了子名称空间“ Parallelized”。
redcalx

2

这是一个非常有趣的问题,其中两个基本相反的答案都是正确的。这是我们在我的一个项目中进行的讨论的真实示例

我认为到目前为止,有两个非常重要的考虑因素,它们使您想朝着一个方向或另一个方向前进,这要从@Jorg和@Frank的两个答案中得出:

  1. 如果您预期在同一代码上下文中可能需要使用多个同名类的情况,那么这就是不使用同一名称的原因。也就是说,如果您需要使用,hr.Employee但同时又需要通过查看权限security.Employee,它将很快变得很烦人。
  2. 相反,如果您的同名类都具有相同或非常相似的API,那么这就是当您应将同名用于不同名称空间的一个很好的例子。Scala示例就是这样,其中所有Map类都实现相同的接口,或者是彼此的子类。在这种情况下,模棱两可或“混乱”可以说是一件好事,因为用户正确地可以将类或接口的所有实现都视为相同...这是接口和子类的重点。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.