- 浏览: 1704821 次
- 性别:
- 来自: 杭州699号
文章分类
最新评论
-
莫莫摸:
为什么不用dubbo
RCP数据传输模型回顾 -
大胡子爸爸:
String, Class 都实现了Serializable接 ...
RPC框架几行代码就够了 -
lss598018587:
谢谢大神分享,比起新手看复杂的dubbo框架还不如看大神的这一 ...
RPC框架几行代码就够了 -
15606915740:
你好,请问一下。<dubbo:consumer filt ...
Dubbo文档 -
joqk12345:
...
一些设计上的基本常识
转自: http://blog.csdn.net/alexjames_83/archive/2005/04/14/347726.aspx
OO设计中有一个重要原则:结构(mechanism)与策略(Policy)分离。Interface是实践这一原则的重要途径。
从概念上将,Interface用来扮演使用者与实现者之间的契约(contract)。我们使用Interface来定义一组Abstract Operation(method)的集合,用实现类实现这些Interface,调用者通过访问这些Interface的实现类所提供的相应服务。调用者并不关心接口是由谁实现的,它看到的只有Interface本身。
Contract
|-----------| Realize |-------------|
| Class A |-------->| Interface A | Access |------|
|-----------| |-------------|<-------|Caller|
|+Operation1()| |------|
|-----------| Realize |+Operation2()|
| Class B |-------->|-------------|
|-----------|
在定义Interface Specification的时候一定要基于上述Interface的意义来进行。而Java的Interface设计就存在着缺陷。
在Java中,一个Class可以从另外一个Class继承,同时可以实现多个Interface。所以,一个Method既可以从base class那里继承过来,也可以是要实现的Interface Method。而Java的Method Signature由Method Name和Parameter List组成。这就意味着Java无从区分来自于Class继承体系的method和Interface实现体系的method。这会造成很大的困惑。
例如,我们从Vendor A和Vendor B处各购买了一套Java包,我们不妨称之为Package A和Package B。在Package A中,有一套Container的继承
体系,在Package B中有一套通过实现IContainer构成的一套Container体系。如下图所示:
(见附件)
现在我们想增加一个新的容器类PowerVector,对于它,我们即想让其相容与Package A的Container架构,又想让其相容于Package B的Container体系。于是,我们让PowerVector从Vector类继承,同时让其实现IContainer。正如图中所画的那样。
这是一个再自然不过的方法。但问题是,PowerVector从Vector(Container)继承了size方法,同时要实现IContainer的size方法。这个来自两个供应商的两套体系价构的方法在Java中有着完全相同的签名,但却包含完全不同的含义。在Package A的Container体系中,size方法用来查询Container的容量(capability);在Package B中,size方法却被用来计算Container所存放的Item的数量。
而使用Java,由于size方法的签名完全相同,所以在PowerVector中,你只有两种选择,继承或改写(override)Vector的size方法。如果是继承,那么通过IContainer的size方法就得到了错误的结果。如果为了迎合IContainer而改写,则通过Package A的Container体系来访问size方法时,同样是错误的。
你或许会说:这个很简单,把其中一套体系的size方法改变签名不就行了吗?这是一个没有动脑的建议。想想看,由于Package A和Package B来自于两个不同的供应商,它们在开发各自的Package时,根本不会考虑别人是否会如何命名。而在自己的体系中,已经有大量的类在使用自己体系的定义。修改任何一个都会很麻烦。更何况,你拿到的很有可能是编译后的包,根本就无从改起。
如果你是个Java高手,你或许可以通过一些特殊的架构或方法解决掉这个问题。但那些方法都不是最自然的方法。而一个设计充分的语言,应该让设计师或程序员能够用最自然的方法表现自己的意图。
我们上面所给出的例子并不是一个特殊的情况。而是一种经常会出现的case。只所以Java设计出现了这样的问题,是因为Java语言的设计师根本没有深刻理解在这一问题上的OO思想。
这是因为,虽然Java仅仅允许但继承,但却允许实现多个接口,那么一个Class所拥有的Operation(Method)就会来自于多条线。而每一条线都有可能由不同的人,不同的公司来设计,虽然对于Class或Interface的签名,由于Sun建议了Package的命名规则,所以,可以避免签名冲突,但对于method的签名,按照Java的method signature规则,产生冲突的可能性是很大的。而相同签名的method,很有可能具有很大差异的含义。
从本质上说,当如果一个Class继承了另外一个Class,同时实现了一个或多个Interface,或者,仅仅实现了多个接口时,如果这些Interface和Class具有相同签名的method(按照Java的签名原则),这些method应该被看作具有不同签名的method。应该对其分别看待。比如:
// 下面的代码即不是Java code也不是C++ code,而是一种伪code
public class Base{
public int foo() { return 0; }
}
public interface IFoo{
int foo();
}
public class Derived extends Base implements IFoo
{
// 还存在着一个继承自Base的foo method
int IFoo::foo() { return 10; } //需要指明这是IFoo的foo mothed的实现
}
这样,如果我们有这样一段code,
Derived derived = new Derived;
derived.foo(); // 调用的是Base::foo
IFoo ifoo = (IFoo)derived;
ifoo.foo(); // 调用的是Base::IFoo::foo
另外一个例子:
public interface IFoo1{
int foo();
}
public interface IFoo2{
int foo();
}
public class Foo implements IFoo1, IFoo2
{
//需要分别指明IFoo1和IFoo2的foo mothed实现
int IFoo1::foo() { return 0; }
int IFoo2::foo() { return 10; }
}
Foo obj = new Foo;
IFoo1 foo1 = (IFoo1)obj;
foo1.foo(); // 调用的是Foo::IFoo1::foo
IFoo2 foo2 = (IFoo1)obj;
foo2.foo(); // 调用的是Foo::IFoo2::foo
对于这种解决方案,随之产生的一个问题是,如果我们在一个class内实现了一个Interface,而我们基于这个class的引用来访问这个class的instance时,我们如何访问这些interface methods?
我对答案就是:不允许访问,因为,这些方法的存在是因为这个class实现了方法所在的接口,这些方法的实现定义了契约履行的行为,也就是说他们是契约被执行过程的一部分。既然接口是调用者和服务提供者之间的契约,那么访问它们的途径当然也应该通过接口。这是完全符合Interface的语义的。如果你真想访问这些interface method,可以把它们包装为class method。
由于接口允许扩展其它接口,就会产生这样的问题:
public interface IFooBase{
int foo();
}
public interface IFoo1 extends IFooBase{
void draw();
}
public interface IFoo2 extends IFooBase{
void move();
}
public class Foo implements IFoo1, IFoo2 { ... }
这种情况下,IFoo1和IFoo2都从IFooBase那继承了foo方法,如果我们在class Foo中分别为这两个foo进行实现,就会存在问题,比如:
Foo obj = new Foo;
IFooBase foo0 = (IFoo1) obj;
foo0.foo(); //???到底调用的哪个实现??
所以,对于这种情况,我们必须规定,如果两个或多个接口都扩展了某个接口,并且随后这些接口被同一个Class实现的时候,你不能分别为其实现,而是要直接指定base interface的方法实现。以上例为例,其实现应该为:
public class Foo implements IFoo1, IFoo2 {
int IFooBase::foo() { return 0; }
void IFoo1::draw() { ... }
void IFoo2::move() { ... }
}
对于这种情况,还有一种表现形式:
public class Base implements IFoo1{
int IFoo1::foo() { return 0; } // 这里,我们只需要指定IFoo1即可
void IFoo1::draw() { ... }
}
public class Derived extends Base implements IFoo2{
void IFoo2::move() { ... }
// 对于IFoo2::foo(),由于在Base中已经实现,
// 我们可以继承其实现,也可以改写它。
// 但在改写的时候,我们使用什么名字呢?
// IFoo1::foo()? IFoo2::foo()? 还是IFooBase::foo()
}
这种情况下,随意指定范围会引起很大的困惑。为了避免这种问题,我们可以做出这样的规定:当我们在实现一个接口的时候,对于每一个method,我们都指定其定义所在的Interface,而不是扩展后的Interface。按照这个规定,我们上例的实现应该为:
public class Base implements IFoo1{
int IFooBase::foo() { return 0; } // 这里,我们必须指定IFooBase
void IFoo1::draw() { ... }
}
public class Derived extends Base implements IFoo2{
void IFoo2::move() { ... }
// 对于IFooBase::foo(),由于在Base中已经实现,
// 我们可以继承其实现,也可以改写它。
// 但在改写的时候,我们仍然必须使用IFooBase::foo()
}
上述的一切就形成了一个完整的Interface解决方案,按照这种方案,设计师及程序员就可以自然、直观、正确的使用接口进行OO的架构设计。同时也期望Java能够在未来的版本中解决掉这个缺陷。
同意
OO设计中有一个重要原则:结构(mechanism)与策略(Policy)分离。Interface是实践这一原则的重要途径。
从概念上将,Interface用来扮演使用者与实现者之间的契约(contract)。我们使用Interface来定义一组Abstract Operation(method)的集合,用实现类实现这些Interface,调用者通过访问这些Interface的实现类所提供的相应服务。调用者并不关心接口是由谁实现的,它看到的只有Interface本身。
Contract
|-----------| Realize |-------------|
| Class A |-------->| Interface A | Access |------|
|-----------| |-------------|<-------|Caller|
|+Operation1()| |------|
|-----------| Realize |+Operation2()|
| Class B |-------->|-------------|
|-----------|
在定义Interface Specification的时候一定要基于上述Interface的意义来进行。而Java的Interface设计就存在着缺陷。
在Java中,一个Class可以从另外一个Class继承,同时可以实现多个Interface。所以,一个Method既可以从base class那里继承过来,也可以是要实现的Interface Method。而Java的Method Signature由Method Name和Parameter List组成。这就意味着Java无从区分来自于Class继承体系的method和Interface实现体系的method。这会造成很大的困惑。
例如,我们从Vendor A和Vendor B处各购买了一套Java包,我们不妨称之为Package A和Package B。在Package A中,有一套Container的继承
体系,在Package B中有一套通过实现IContainer构成的一套Container体系。如下图所示:
(见附件)
现在我们想增加一个新的容器类PowerVector,对于它,我们即想让其相容与Package A的Container架构,又想让其相容于Package B的Container体系。于是,我们让PowerVector从Vector类继承,同时让其实现IContainer。正如图中所画的那样。
这是一个再自然不过的方法。但问题是,PowerVector从Vector(Container)继承了size方法,同时要实现IContainer的size方法。这个来自两个供应商的两套体系价构的方法在Java中有着完全相同的签名,但却包含完全不同的含义。在Package A的Container体系中,size方法用来查询Container的容量(capability);在Package B中,size方法却被用来计算Container所存放的Item的数量。
而使用Java,由于size方法的签名完全相同,所以在PowerVector中,你只有两种选择,继承或改写(override)Vector的size方法。如果是继承,那么通过IContainer的size方法就得到了错误的结果。如果为了迎合IContainer而改写,则通过Package A的Container体系来访问size方法时,同样是错误的。
你或许会说:这个很简单,把其中一套体系的size方法改变签名不就行了吗?这是一个没有动脑的建议。想想看,由于Package A和Package B来自于两个不同的供应商,它们在开发各自的Package时,根本不会考虑别人是否会如何命名。而在自己的体系中,已经有大量的类在使用自己体系的定义。修改任何一个都会很麻烦。更何况,你拿到的很有可能是编译后的包,根本就无从改起。
如果你是个Java高手,你或许可以通过一些特殊的架构或方法解决掉这个问题。但那些方法都不是最自然的方法。而一个设计充分的语言,应该让设计师或程序员能够用最自然的方法表现自己的意图。
我们上面所给出的例子并不是一个特殊的情况。而是一种经常会出现的case。只所以Java设计出现了这样的问题,是因为Java语言的设计师根本没有深刻理解在这一问题上的OO思想。
这是因为,虽然Java仅仅允许但继承,但却允许实现多个接口,那么一个Class所拥有的Operation(Method)就会来自于多条线。而每一条线都有可能由不同的人,不同的公司来设计,虽然对于Class或Interface的签名,由于Sun建议了Package的命名规则,所以,可以避免签名冲突,但对于method的签名,按照Java的method signature规则,产生冲突的可能性是很大的。而相同签名的method,很有可能具有很大差异的含义。
从本质上说,当如果一个Class继承了另外一个Class,同时实现了一个或多个Interface,或者,仅仅实现了多个接口时,如果这些Interface和Class具有相同签名的method(按照Java的签名原则),这些method应该被看作具有不同签名的method。应该对其分别看待。比如:
// 下面的代码即不是Java code也不是C++ code,而是一种伪code
public class Base{
public int foo() { return 0; }
}
public interface IFoo{
int foo();
}
public class Derived extends Base implements IFoo
{
// 还存在着一个继承自Base的foo method
int IFoo::foo() { return 10; } //需要指明这是IFoo的foo mothed的实现
}
这样,如果我们有这样一段code,
Derived derived = new Derived;
derived.foo(); // 调用的是Base::foo
IFoo ifoo = (IFoo)derived;
ifoo.foo(); // 调用的是Base::IFoo::foo
另外一个例子:
public interface IFoo1{
int foo();
}
public interface IFoo2{
int foo();
}
public class Foo implements IFoo1, IFoo2
{
//需要分别指明IFoo1和IFoo2的foo mothed实现
int IFoo1::foo() { return 0; }
int IFoo2::foo() { return 10; }
}
Foo obj = new Foo;
IFoo1 foo1 = (IFoo1)obj;
foo1.foo(); // 调用的是Foo::IFoo1::foo
IFoo2 foo2 = (IFoo1)obj;
foo2.foo(); // 调用的是Foo::IFoo2::foo
对于这种解决方案,随之产生的一个问题是,如果我们在一个class内实现了一个Interface,而我们基于这个class的引用来访问这个class的instance时,我们如何访问这些interface methods?
我对答案就是:不允许访问,因为,这些方法的存在是因为这个class实现了方法所在的接口,这些方法的实现定义了契约履行的行为,也就是说他们是契约被执行过程的一部分。既然接口是调用者和服务提供者之间的契约,那么访问它们的途径当然也应该通过接口。这是完全符合Interface的语义的。如果你真想访问这些interface method,可以把它们包装为class method。
由于接口允许扩展其它接口,就会产生这样的问题:
public interface IFooBase{
int foo();
}
public interface IFoo1 extends IFooBase{
void draw();
}
public interface IFoo2 extends IFooBase{
void move();
}
public class Foo implements IFoo1, IFoo2 { ... }
这种情况下,IFoo1和IFoo2都从IFooBase那继承了foo方法,如果我们在class Foo中分别为这两个foo进行实现,就会存在问题,比如:
Foo obj = new Foo;
IFooBase foo0 = (IFoo1) obj;
foo0.foo(); //???到底调用的哪个实现??
所以,对于这种情况,我们必须规定,如果两个或多个接口都扩展了某个接口,并且随后这些接口被同一个Class实现的时候,你不能分别为其实现,而是要直接指定base interface的方法实现。以上例为例,其实现应该为:
public class Foo implements IFoo1, IFoo2 {
int IFooBase::foo() { return 0; }
void IFoo1::draw() { ... }
void IFoo2::move() { ... }
}
对于这种情况,还有一种表现形式:
public class Base implements IFoo1{
int IFoo1::foo() { return 0; } // 这里,我们只需要指定IFoo1即可
void IFoo1::draw() { ... }
}
public class Derived extends Base implements IFoo2{
void IFoo2::move() { ... }
// 对于IFoo2::foo(),由于在Base中已经实现,
// 我们可以继承其实现,也可以改写它。
// 但在改写的时候,我们使用什么名字呢?
// IFoo1::foo()? IFoo2::foo()? 还是IFooBase::foo()
}
这种情况下,随意指定范围会引起很大的困惑。为了避免这种问题,我们可以做出这样的规定:当我们在实现一个接口的时候,对于每一个method,我们都指定其定义所在的Interface,而不是扩展后的Interface。按照这个规定,我们上例的实现应该为:
public class Base implements IFoo1{
int IFooBase::foo() { return 0; } // 这里,我们必须指定IFooBase
void IFoo1::draw() { ... }
}
public class Derived extends Base implements IFoo2{
void IFoo2::move() { ... }
// 对于IFooBase::foo(),由于在Base中已经实现,
// 我们可以继承其实现,也可以改写它。
// 但在改写的时候,我们仍然必须使用IFooBase::foo()
}
上述的一切就形成了一个完整的Interface解决方案,按照这种方案,设计师及程序员就可以自然、直观、正确的使用接口进行OO的架构设计。同时也期望Java能够在未来的版本中解决掉这个缺陷。
评论
2 楼
dylan0514sina.cn
2013-04-02
andylo25 写道
个人觉得java的设计师应该考虑到了这些情况,因为这个现象或者说bug很明显,不可能没发现,但是为什么不去“修复”,个人觉得是一个取舍的问题,操作简单,代码清晰,约定是java的一个比较明显的特点。是否为了一个低概率出现甚至可以通过其他方式避免的情况致使代码像C++一样“难看”,这是有待商榷的。因为这种(IFooBase::foo())写法很不符合java的风格。
同意
1 楼
andylo25
2012-12-10
个人觉得java的设计师应该考虑到了这些情况,因为这个现象或者说bug很明显,不可能没发现,但是为什么不去“修复”,个人觉得是一个取舍的问题,操作简单,代码清晰,约定是java的一个比较明显的特点。是否为了一个低概率出现甚至可以通过其他方式避免的情况致使代码像C++一样“难看”,这是有待商榷的。因为这种(IFooBase::foo())写法很不符合java的风格。
发表评论
-
能力成长模型
2012-05-09 00:28 23017最近看了温伯格1986年出版的《技术领导之路》, 很老的书,讲 ... -
以HTTL为例讲讲模块分包&领域模型&扩展框架
2011-10-09 20:08 16816注:该博客内容已加入 ... -
使用Map参数的Webx3扩展
2011-08-28 02:10 5935因Webx3是开源的,所以把这个简单的Webx3扩展发在博客上 ... -
Netty内存泄露
2011-08-02 20:09 24980转于自己在公司的Blog: ... -
Grizzly和Netty以及Mina简单性能对比
2011-07-17 02:48 29767转于自己在公司的Blog: http://pt.alibaba ... -
RPC框架几行代码就够了
2011-07-14 00:34 90293转于自己在公司的Blog: http://pt.alibaba ... -
魔鬼在细节中
2011-05-24 14:50 32255转于自己在公司的Blog: ... -
Dubbo扩展点重构
2011-05-12 22:09 38913转于自己在公司的Blog: http://pt.alibaba ... -
配置设计
2011-03-09 23:41 23613转于自己在公司的Blog: ... -
[转]HTML5设计原理
2011-03-09 22:57 7839Jeremy Keith在 Fronteers 2010 ... -
Hessian序列化不设SerializerFactory性能问题
2010-12-27 11:38 6490转于自己在公司的Blog: http://pt.alibaba ... -
动态代理方案性能对比
2010-11-17 21:38 46254转于自己在公司的Blog: http://pt.alibaba ... -
防痴呆设计
2010-11-05 18:58 17703转于自己在公司的Blog: ... -
负载均衡扩展接口重构
2010-11-05 18:53 8768转于自己在公司的Blog: ... -
分布式服务框架常被质疑的价值
2010-11-05 18:52 5743转于自己在公司的Blog: http://pt.alibaba ... -
Hessian3.2.1在序列化32.5k字符串时的问题
2010-11-05 18:49 7289转于自己在公司的Blog: http://pt.alibaba ... -
一些设计上的基本常识
2010-07-05 19:28 27806转于自己在公司的Blog: ... -
谈谈扩充式扩展与增量式扩展
2010-06-12 19:46 19427转于自己在公司的Blog: http://pt.alibaba ... -
Scaling Architecture
2010-02-25 10:31 4129Scaling Second Life: http://p ... -
EBay SOA
2010-02-23 18:23 4811EBay SOA PPT
相关推荐
为了弥补这一缺陷,Java引入了JNI(Java Native Interface)技术,允许Java代码直接调用本地C/C++代码,从而实现更深层次的操作系统访问和性能优化。 #### 二、Java调用C/C++的基础概念 在讨论具体过程之前,我们...
1. **获取网络接口**:使用Java的`java.net.NetworkInterface`类,可以获取本地计算机的所有网络接口,这包括了我们需要操作的网卡信息。 2. **构造ARP包**:利用`javax.crypto`和`java.net`包中的类,我们可以构建...
JAVA外文文献翻译 Java是一种计算机编程语言,但它为什么如此重要,... Java的出现解决了CGI程序的缺陷,提供了更好的用户体验和安全性。因此,Java是一种非常重要的编程语言,对于计算机编程领域产生了深远的影响。
* invert:使转位、倒转 * diamond:菱形 * password:密码、口令 这些单词都是Java开发中常用的随机数单词,了解这些单词对于编写Java程序非常重要。 第九章:其他概念 * change:交换、互换 * password:口令、...
本文档列出了FindBugs报告的标准缺陷模式,包括正确性、安全性、性能、多线程、坏味道代码等多个方面。 正确性: 1. AEC: Call to equals() with null argument:调用equals()方法时,参数为空,这可能会导致...
类是面向对象编程的基础,在Java中,所有程序都是通过类来组织的。 ### 静态:`static` `static`关键字用来声明静态变量、静态方法或静态代码块。静态成员属于类本身,而不是类的实例,这意味着它们可以通过类名...
这种方法不仅减少了代码在不同平台间的移植工作量,还有效避免了类型匹配和安全控制方面的问题,因为Java安全模型已经扩展以支持非系统类加载和本地方法调用。 #### 三、解决本机平台接口调用问题 尽管Java的跨...
- **simple**:这里可能是指简化版的概念,但在Java中一般不作为关键字使用。 ### 第四章:选择结构 - **case**:在`switch`语句中用来匹配特定的值。 - **default**:`switch`语句中的默认分支,当没有其他`case`...
29. **bug**:在编程中,"bug"指的是代码中的错误或缺陷。 30. **debug**:调试是查找和修复代码错误的过程。 31. **step**:在调试中,"step"指的是逐行执行代码。 32. **error**:错误是程序执行期间遇到的问题...
在Java中,我们经常会遇到实体与模型间的属性不匹配问题,例如在User实体中有一个name属性,而在Employee模型中对应的属性是ename。这时,如果我们想将User对象转换为Employee对象,或者反之,就需要解决属性名不...
在Java中,`enum`(枚举)是一种特殊的类,用于定义一组常量值。它可以帮助开发者限制一个变量的可能值,使其只能从预定义的一组选项中选择。枚举提供了一种更安全、更清晰的方式来处理一组固定的值。 #### 二、...
为了弥补这一缺陷,Java Native Interface (JNI) 应运而生,它允许Java代码调用C/C++编写的本地代码。本文旨在提供一份详尽的JNI学习指南,帮助初学者理解如何在Java中调用C/C++接口。 #### 二、JNI的基本原理 JNI...
Bug:缺陷错误,指的是某个程序或系统中的错误或缺陷。 Build:编连(专指编译和连接),指的是某个编译或连接操作。 Built-in:内建、内置,指的是某个内建或内置的组件或功能。 Business:业务、商务(看场合),指...
本方法的核心在于利用JAVA的JNI(Java Native Interface)技术将解密过程及中间数据封装在本地链接库中,从而提高了加密的安全级别。具体来说,解密过程及与之相关的类和数据均被隐藏在本地链接库内,这使得即使反...
然而这种方法存在明显的缺陷:在实际使用过程中,必须手动进行类型转换,而且如果转换错误,编译器不会报错,错误会在运行时以异常的形式出现,从而构成潜在的安全隐患。 #### 二、泛型的好处 - **类型安全**:...
- **customer** ['kʌstəmə]:客户或用户,虽然这个词在 Java 中没有直接的应用场景,但在描述应用程序的目标用户时可能会提到。 - **birthday** ['bə:θdei]:生日,在处理日期或用户信息时可能会用到。 - **...
实验结果显示,该方法在Java加密相关的API及其使用代码上进行了验证,证明了该方法能在一定程度上自动发现API误用缺陷,提高了检测效率和准确性。尽管这种方法取得了一定的成功,但仍然面临挑战,例如如何处理API的...
Java的核心概念包括类、对象、接口、包、异常处理、多线程、IO流、网络编程、集合框架(如ArrayList、LinkedList、HashMap等)、反射机制和JNI(Java Native Interface)。Java SE(标准版)用于桌面应用,Java EE...