`

关于java中的“包”与C#中的“命名空间”的简单认识

    博客分类:
  • Java
阅读更多

Package vs. Namespace

我们知道,重用性(reusebility)是软件工程中一个非常重要的目标。重用,不仅仅指自己所写的软件(代码、组件等等)可以被重复利用;更广义的重用是指不同的人,不同的团队,不同的公司之间可以互相利用别人的成果。另外,对于大型软件,往往是由多个团队共同开发的,这些团队有可能分布于不同的城市、地区、甚至国家。由于这些原因,名字管理成为一个非常重要的因素。

由于C语言本身不提供名字管理的机制(C语言的static命名解决的是可见性问题,这些名字不会输出给外部,但我们要讨论的名字空间和这个问题并不完全一样),为了解决名字冲突的问题,大家一般会选取加前缀的方法;而前缀规则往往是: ${project name}_${name};更加安全的命名规则将前缀分了更多的级别:${project name}_${module name}_${name}。

这种方案在现实生活中有大量类似的例子。比如:中国的很多城市都有滨河路,如果你谈话的对象都明白你所指的城市,你只需要说滨河路,大家都会明白你的所指。但如果情况不是这样,你就需要加上前缀,说明这到底是乐山的滨河路,还是成都的滨河路。你在邮寄信件的时候,这一点体现的最为直接。

所以,如果存在一个全局的管理,C语言的这种方案应该是非常有效的。但它的缺陷是,这可能会造成很长的名字,而每次在引用一个名字的时候你都必须给出全名。

这是一件非常麻烦的事情。你不妨想象一下,明明大家都知道你所谈的是乐山的滨河路,但你不得不在每次谈到它的时候,都要说“中国四川省乐山市滨河路”,你会多么的痛苦。

为了解决这类问题,并给出一个行之有效的管理方案。随后的编程语言,无论是C++,Java还是C#都提供了自己的名字管理的机制。这些方案在本质上有其统一的思想,但操作方式存在着一定的差异。

在前面C语言的方案里,本身体现了分级管理的方式。分级管理是一种非常自然而有效的手段。比如,互联网的域名。它通过分级的命名保证了一个名字的全局唯一性,其排列方式是从小范围到大范围(这既是因为西方的书写习惯,也是为了方便。其实从这一点上,我们可以发现,如果一个人的预读习惯是从左到右的话,从小到大的排布方式则非常便于节省时间,比如 “滨河路,乐山市,四川省,中国”。在我们从左边的信息已经知道我们的所指时,则可以跳过或忽略后面的信息。而从大到小的排布方式则可以避免错误,因为我们首先了解了限定条件,最后读到滨河路的时候,我们已经确定我们的所指了) 。我们可以在前面加上名字,指定更小的范围。比如:wsd.wmsg.sps.motorola.com 说明这是motorola公司的SPS部门的wmsg部门的wsd组。

Java使用这种方式来命名包(Package),只不过把书写方式反过来。这种方式可以非常有效的保证命名的统一。比如,一个名为mlca的项目的mmi模块包可以命名为:com.motorola.sps.wmsg.wsd.mlca.mmi,而其engine模块包可以命名为:com.motorola.sps.wmsg.wsd.mlca.engine。

这样,当不同的团队,公司之间的代码放在一起进行使用时,在一个名字不冲突的情况下,我们只需要简单的使用它。当引起冲突的时候,我们指定其全名就行了。比如,上述的两个包中都有一个名为Message的class,如果我们的另外一个package中的某个class要同时使用这两个包,在引用 Message类的时候,我们需要指明它来自于哪个包。如下:


import com.motorola.sps.wmsg.wsd.mlca.mmi;
import com.motorola.sps.wmsg.wsd.mlca.engine;


// 我们需要指明class Message来自于哪个包.
public class Foo extends com.motorola.sps.wmsg.mlca.mlca.mmi.Message {
...
}

而C++和C#则提供了namespace的概念来支持这种方式。你可以在全局的空间内指定自己的namespace,然后还可以在某个 namespace内制定更小范围的namespace。虽然C++和C#本身没有推荐任何namespace的命名方式(其实反域名的方式也是Java推荐的,并非强制),但我们也可以使用上述方式。比如下面的C# code:


namespace com.motorola.sps.wmsg.wsd.mlca.mmi
{
// MMI Stuff
...
}

namespace com.motorola.sps.wmsg.wsd.mlca.engine
{
// Engine Stuff
...
}

当我们同时使用这两个模块时,如果出现名字冲突,也许要通过指定namespace来指明。比如:


class Foo: com.motorola.sps.wmsg.wsd.mlca.mmi.Message
{
...
}

Java的 package本身没有子包的概念,所有package都是并列的关系,没有谁包含谁的问题。比如:org.dominoo.action和 org.dominoo.action.asl之间绝对没有包与子包的关系。它们是各自独立的包,各自拥有自己的class/interface的集合。在org.dominoo.action.asl的某个java文件里,如果想引用org.dominoo.action里的某个class/interface,则必须import org.dominoo.action。

C++/C#的namespace方案则不然,一个namespace可以有自己的sub-namespace,我们不妨将namespace也称为package,那么C++/C#的package之间就可能存在包与子包的关系. 比如:


namespace org.dominoo
{
   namespace action
   {
     namespace asl
     {
       ....
     }
   }

   namespace constraint
   {
     namespace ocl
     {
       ....
     }
   }
}

在这个例子中,action和constraint都是org.dominoo的子包,而它们又各自拥有自己的子包asl和ocl。

所以,对于一个全局的名字空间,C语言无法直接进行名字空间分离,而Java则可以从全局的名字空间里分离出独立的名字空间,但C++/C#则可以进一步将各个名字空间进行进一步分离。 如下图:


|------------------------|
| global namespace |
|                              |
|                              |
|                              |
|------------------------|
         C 语言


|------------------------|
| global namespace |
|                              |
| |---| |---| |---|         |
| | A | | B | | C |        |
| |---| |---| |---|         |
|------------------------|
       Java 语言


|----------------------------|
| global namespace      |
|                                   |
| |-------------| |--------|  |
| | A             | | B       |  |
| | |---| |---|  | | |---|  |  |
| | | C | | D | | | | E |  |  |
| | |---| |---|  | | |---|  |  |
| |-------------| |--------|  |
|----------------------------|
       C++/C# 语言

所以,Java的Package方案只对全局的名字空间进行了一次划分,本质上只是为语言提供了一个命名前缀方案,只是通过命名前缀的分级管理来保证名字的唯一性。 它唯一的作用就是为了避免名字冲突。

而C++/C#则提供了对任何一个空间进行再次划分的能力。在Java中org.dominoo和org.dominoo.asl之间是完全没有包含关系的各自独立的包,但在C++/C#中,dominoo.asl则和明显是dominoo的一个子包。

事实上,如果仅仅为了避免命名冲突,像C++/C#这样复杂的方案并无必要,Java的方案就足够了。 但C++/C#这种方案可以带来其它的便利:

1、软件开发的本质就是自上而下依次分解的,每一层都有自己的定义,并且这种定义可以作为下一层所有子系统的公共服务,多层次的树状结构符合这种逻辑。C++/C#方案用最自然的方式满足了这种划分关系。事实上,这种方案和文件管理的思路是一样的。

2、一个程序一旦using哪个namespace,就可以通过它向下访问它的子包,而无需指出全路经。比如,在上面的图中,如果一个程序写了 using namespace A,则它在访问C包中的class Foo时,只需要写C::Foo,而不需要写全路径::A::C::Foo。在Java中,由于A,C是并列的关系,为了访问C中的内容,必须明确指出import C。然后在访问Foo而产生名字冲突的情况下,必须指出全路径。

3、当程序身处某个包的时候,在不产生名字冲突的情况下,可以直接访问外部包中的定义。由于Java包的层次只有一层,所以Java只能直接访问global namespace中的定义,任何其它包中的定义,必须通过import才能够访问。

毫无疑问,C++/C#的方案更加强大灵活,但也更复杂。而复杂的东西往往让使用者更容易犯错误。孰优孰劣,你自己判断吧。

分享到:
评论

相关推荐

    我对C#的认识及学习心得

    例如,C#引入了命名空间(Namespace)的概念,用于组织代码,避免命名冲突。同时,C#中的类(Class)是面向对象编程的基础,通过定义类来创建对象,并通过方法(Method)和属性(Property)实现对象的行为和状态。 ...

    c#入门的大体认识,有助于你在学习他之前必须的对他的了解!

    3. **命名空间**:C#使用命名空间(namespace)来组织代码,类似于Java的包。例如,`using System;`导入了包含基本C#类的System命名空间。 4. **接口**:C#中的接口(interface)与Java中的接口类似,定义了一组...

    一周学会C# 一周学会C#

    8. **LINQ(Language Integrated Query)**:这是一种在C#中集成的数据查询机制,使得查询数据变得更简单、更直观。 9. **异步编程**:C#支持异步编程模型,使用async和await关键字,可以使代码在等待I/O操作完成时...

    有关C#和数据库的培训WorkReport

    本次培训详细介绍了如何使用ADO.NET组件中的`System.Data.SqlClient`、`System.Data.OleDb`和`System.Data.OracleClient`命名空间来分别连接SQL Server、Access和Oracle数据库。 - **DbConnection**: 创建数据库...

    C#程序设计入门与实例

    最后,为了提升代码的可维护性和复用性,你将学习到C#中的命名空间、库引用以及如何创建和使用自己的类库。 总的来说,《C#程序设计入门与实例》将帮助你建立起坚实的C#编程基础,并通过丰富的实例加深理解。无论你...

    C#入门经典 及代码

    C#中的程序始于`using`语句,它用于引入命名空间,例如`System`,这包含了大部分C#的标准库。接下来是`class`关键字,用于定义类,这是C#面向对象编程的核心。类可以包含方法(函数)和字段(变量)。例如: ```...

    W3SCHOOL的C#编程基础-很棒

    **:引入了System命名空间,包含了Console类。 - **class Program**:定义了一个名为Program的类。 - **static void Main(string[] args)**:定义了程序的入口点方法Main。 - **Console.WriteLine("Hello World!");*...

    C#微软培训资料

    14.4 继承中关于属性的一些问题.169 14.5 小 结 .172 第四部分 深入了解 C#.174 第十五章 接 口 .174 15.1 组件编程技术 .174 15.2 接 口 定 义 .177 15.3 接口的成员 .178 15.4 接口的实现 .182 ...

    深入.NET平台和C#编程【第一章】

    C#是微软为.NET Framework设计的一种面向对象的编程语言,它在语法上与Java和C++有相似之处,但又具有许多独特的特性。在第一章中,你将会学习到C#的基本语法,包括变量声明、数据类型、控制流(如if语句、for循环和...

    《一周学会c#》快速学习c#

    4. **命名空间和引用**:C#中的命名空间组织代码结构,避免命名冲突。通过using关键字,可以方便地引入所需命名空间。同时,理解引用的概念,了解如何添加和管理项目引用,是进行C#编程的基础。 5. **函数与方法**...

    传智播客2012年C#基础班笔记(第六天)苏坤主讲

    - **类与命名空间**:在C#中,类是面向对象编程的基本单元,命名空间则是为了防止命名冲突而设计的一种机制。一个命名空间可以包含多个类,每个类又可以包含多个方法。 - **Main方法**:在C#程序中,`Main`方法是...

    C#的帮助文档pdf格式

    《C#编程全面指南》 C#是一种面向对象的编程...通过这份C#教学文档,读者将能够逐步建立起对C#的全面认识,并掌握其核心概念和技术,从而在实际开发中游刃有余。无论是初学者还是经验丰富的开发者,都能从中受益匪浅。

    C#语言自学总结 Day01-c#

    #### 二、C#与Java的区别 - **C#**: C#是一种面向对象的编程语言,必须在.NET平台下运行。C#是.NET平台的主要语言之一,尽管.NET支持多种语言(如VB.NET),但C#通常被视为主要的选择。 - **Java**: Java既是编程...

    c#学习笔记.txt

    接口可以是命名空间或类的成员,并且可以包含下列成员的签名:方法属性 索引器. 一个接口可从一个或多个基接口继承。接口可由类实现。实现的接口的标识符出现在类的基列表中。被继承的接口称为该接口的显式基接口。...

    C#.NET的入门教程,课本同步教程

    3. **命名空间和引用**:学习如何使用using关键字引入.NET框架中的类库,以及理解命名空间的作用。 4. **方法和重载**:学习如何定义和调用方法,以及理解方法的重载概念。 5. **异常处理**:掌握try-catch块的...

    应用JAVA对操作系统中的多个进程进行调度模拟,包括先来先服务,最早时间优先、多级队列等,并进行了可视化界面实现.zip

    C#的`System.Threading`命名空间提供了线程和锁的相关类,`System.Windows.Forms`或`Windows.UI.Xaml`可用来创建GUI,实现类似的功能。 这个项目不仅涉及基础的进程调度理论,还实践了Java编程和GUI设计,对于理解...

    Visual C#.net教学PPT

    学习使用C#中的命名空间、Console类和I/O控制,为实际开发打下基础。 4. **"Hello C#"示例**:通过编写简单的"Hello C#"控制台程序,学习C#控制台应用程序的开发流程,包括新建项目、编写、编译和运行代码。 5. **...

Global site tag (gtag.js) - Google Analytics