论坛首页 Java企业应用论坛

基础知识: 需求!

浏览 110163 次
该帖已经被评为精华帖
作者 正文
   发表时间:2004-08-21  
potian 写道
我明白了



假设X最早是为了集群环境建立的,内部使用了JDBC来建立singleton,现在我不想要那个JDBC的依赖,怎么办呢

第一个程序里面继续还要JDBC

请给出你使用构造函数的伪码, 我来充当一个转换器, 来给你转换成使用静态工厂.

所以能不能你现在先不要问我怎么办? 我不是很清楚你的意思. 还是请展示一下你怎么办.

我的一个基本论点: 构造函数能达到的灵活性, 静态工厂都达得到.


如果能够推翻这点, 那么, 我的理论自然被推翻.
0 请登录后投票
   发表时间:2004-08-21  
嘿嘿, 你们上夜班, 偶来上早班, 继续继续:

偶不知道potian 说的JDBC集群伪代码怎么写, 就用偶用过的JNDI集群来写伪代码吧:

假设ajoo写了一个A package, 里面有这样一个类, 设计的时候是采用JNDI做集群的singleton:
public class X implements I {
    private X (); {
        //blah blah......;
    }

    public static final I instance();{
        I i = lookupJNDI();;
        if (i == null); {
            i = new X();;
            registerJNDI(i);;
        }
        return i;
    }
}

然后他发布了1.0 package.

potian, readonly开始分别用这个package做项目了.

然则, 项目进行到一半的时候, 多事的readonly发现他的项目更本不需要集群, 于是小心翼翼地对ajoo说, 你的那个X.instance()可以改一下吗, 改成单个JVM的singleton, 因为偶的项目里面不需要用JNDI.

试问: ajoo如何修改他的X.instance(), 发布一个1.1package, 即不会影响potian的集群项目, 又不用让readonly修改他原来写的一堆X.instance()代码呢?
0 请登录后投票
   发表时间:2004-08-21  
看了这个帖子,每一个回帖都是长篇累犊!!!佩服你们!!
其实bean组件已经是java的基本框架之一,这是勿庸置疑的!!bean的特点及其重要性是构成组件框架的基础,正是因为bean本身单纯,形式较统一(getter,setter)的特点使得对象的组装,属性填充达到最大的灵活性。没有这些bean /pojo的特点,何来我们使用的spring,hibernate,webwork。我实在不敢想象没了这些咚咚,我们开发java需要多少人力物力!!
至于public比工厂模式的instance谁好谁坏,我想如果任何一个使用过spring,webwork等优秀框架的人都不会轻易说出他还继续留恋instance。照着争论观点画一下图:


public interface I{
    public void execute();;
}

public class A implements I{
}

public class B implements I{
}
client---使用特定的A对象---->容器beanfactory--->调用Apublic 构造器
client---使用特定的B对象---->容器beanFacoryu---->调用Bpublic 构造器

client---使用特定的A对象---->调用能够产生I对象的factory--->调用A的构造器
client---使用特定的B对象---->调用能够产生I对象的factory--->调用B的构造器

从上图,我无论怎么看,一样都是通过插入一层来解耦两端,那么,我们现在的焦点就可以转到讨论这两种代理的差别。
beanFactory的配置一般通过bean name和class对应,可以通过在容器增加AOP,IoC等功能支持,使得bean的功能在容器中得到增强。
而factory,它的存在最大的理由就是A或者B对象的需要特定的产生,说白了就是不能随便产生,所以就protected构造器,而通过factory对外公开。在现实中,确实有这样的需求,也确实需要这样的模式。然而,这种模式带来的复杂度剧增:看看上图,假设cilent原来有这样的代码:
public class client{
        public void service();{
             I i=factory.getI("A");; 
           i.execute();; 
           return; 
           
       }
}
     //客户需求改变,需要特定实现C:
public class C implements I{
}


对于这个变动,factory该怎么办呢?如果考虑修改facotry代码,new C出来,忽然发现还要修改cilent端代码(客户端也要改!!too bad):
public class client{
       public void service();{
           I i=factory.getI("C");;  //--- too bad 
           i.execute();; 
           return; 
         
      }
}
 //为了解除client端的耦合,修改代码:
public class client{
         public void service();{
                I i=factory.getI("Service");;   //天平逐渐走到bean的模式,往下看就知道了! 
                i.execute();; 
                return; 
         
      } 
}
 factory读取配置文件以动态决定产生I对象的方式,他重构的期望是这样的: 
  factory.xml: 
 <factory> 
    <service name="service" class="C"> 
 </factory> 
 factory读取service配置文件产生C,众观者大o粽獠?是bean吗? 
  NO No No 
  factory的主张观点是,C的产生是特定方式的,比如这样: 
 public class C{
      protected C(int f,int u,int c,int k,int y, int o ,int u );{

          //... 
	}
}


C需要特定构造器,不是吗!!这是factory存在的基本前提,client端需要传进来参数吗?如果需要传,怎么传?先不考虑这个糟糕透顶的假设!factory读取配置文件来搞定这一切,接下来配置文件怎么配?难道还要告诉factory C的构造器参数吗?又开始忙碌定义Xml dtd,定义factory读取xml的巨大功能。 不行,这样就太麻烦了,直接修改ftory代码,让他可以直接new C出来!!呵呵,到这里,我们可以发现,一个特定的实现C的出现,对于factory模式带来多大的困难!而且为了特定的构造器,特地的产生C的方式,需要告诉factory多少信息??????
或者修改多处代码(包括C.client,.facgtory),或者定义一个庞大可怕的factory  以及相对应的xml dtd,它还必须保证足够的可扩展性来对付不断的客户需求。谁敢保证!!??????
分析一下原因,罪魁祸首就是特定构造器的实现。
其实最根本的错误在于限定对象的产生方式,就等于限定client端的使用对象的方式,也从根本上将这种限定作为一种基于client端与对象之间的特定耦合。毫无疑问,这种耦合是灾难性的!!!!
正如potian说得:
引用

对象的产生封装在对象内部也是一个很奇怪的想法,如果在不同情况下,例如需要同步或者需要lazy等等的话,你必须针对修改你的代码,实际上我们最关心的是能够在不同场合使用同一个对象的业务逻辑,而你这样做的话会紧紧因为我们需要不同的对象创建方法而修改对象的代码,这是非常不智的,这也是singleton被视作evil的重要原因之一。(例如singleton,有时候我们希望使用超类的实例,有时候希望使用子类的实例,有的时候我们希望产生单个,有的时候需要产生多个[取singleton单控制点的含义],有的时候需要同步,有的时候不需要同步,在集群的情况我们甚至可能需要数据库来实现唯一化控制,有时候希望缓存,有的时候不需要缓存,有的时候希望增强,有的时候希望采用动态代理产生。所以一般来说,我们希望在一个系统内部最多只有一个入口的singleton,而我们也往往也不打算在其它不同的场合重用这个singleton)
代码的重用是对他业务逻辑的重用,这正是对象的核心价值。所以如同charon所说的,相对而言,我们往往不在乎组装代码的重用性,而是追求业务代码本身可以被按照不同的组装方式使用,例如既可以在EJB下,也可以在普通的Java应用程序中,既可以作为远程传输的对象,因此,我们往往不会去强制对象构造和产生的方式(例如由EJB容器产生,由IOC容器产生、有抽象工厂产生,或者直接由new产生),相反,是把对象的产生交给外部负责,这样才能达到在不同场合下对象最大的可重用性。说个简单的,如果对象的构造方法是私有的,那么现在的很多框架(例如hibernate,JavaBean,EJB等等你就根本不能使用了).这也是现在认为POJO比有特殊要求的对象更好的原因,因为任何一种框架和技术都可以自由选择自己的方式来创建对象。
所有创建型设计模式和IoC容器的使用正是基于这样的假设的,只有对象把构造的责任交给外部来实现,那么我们才能有效地隐藏对象创建的时机、方式、方法,给不修改对象本身的代码而能够使用不同的对象创建方式(包括使用子类,替代类)提供了前提,从而提高对象实现业务逻辑的在不同场合的可重用性。所以不是说一定要用IOC容器或者用抽象工厂,而是可以用这些方法,也可以不用这种方法。而就抽象工厂和IoC容器本身而言,通过它们各自的封装,可以进一步实现对不同具体实现子类的解绑,就更加好了。如果有一天你不想用Pico或者不想用抽象工厂,你可以选择其他更加合适的方法来实现解绑。而这个责任不应该交给对象自己来实现,因为我们根本无法预料这个对象将会以什么样的方式被构造出来,在什么时候构造出来,需要依赖什么其它外部机制构造出来(例如可能依赖数据库,或者依赖串行化),这最好是有外部使用这个对象的环境来决定
最终,我们希望能够让对象的创建、对象的销毁完全脱离对象的使用,垃圾收集器已经为我们提供VM级别的支持,而抽象工厂和工厂方法以及其它构造型设计模式,IoC从模式和框架的角度给我们另一半
你这个已经是每次返回一个新的对象了,我要每次得到同一对象,我该怎么办,你是不是叫我重新再在上面包一个类,把第一次取到的对象缓存起来,以后每次去取那个对象?这个时候你每次new一个还有什么意义,你说可以把它改成单个,那我原先在用的代码怎么办?
再复杂一点,如果是需要在集群的服务器之间保持singleton,有的时候我需要用数据库来持久对象状态,通过数据库来保持唯一性,那你这份代码需要依赖于JDBC,这个时候如果一个普通的应用程序是不是也要依赖于JDBC?
区别在于你的代码是放在本类里面的,你这个类构造会直接依赖于你的环境和当前的假设,别人根本没办法重用你的类
构造函数为什么没有这些缺陷,因为我们可以通过另外一个类来实现和不同环境的结合,而我们正在谈论的这个类本身可以在任何地方使用而不需要修改任何代码.这是非常重要的,这是重用的基础,从我们谈的范围内来说,如果对照OCP,那么它是C,一旦关闭,永不修改。构造函数为什么没有这个问题,因为他自己不对任何构造自己的方法作出额外的假设(构造函数是最最近基本的假设了),而把构造的方法、意图和时机完全交给了外部,如果对照OCP,那么它是O。除非你的静态方法永远等同于new,不然的话,你任何一种实现方式都是对可能的构造方式进行无意义的假设,会限制其他场合对你这份代码重用的可能性,而如果永远等于new,那你这个静态方法还有什么用。
举例来说,假设有3个应用,对一个对象需要三种不同的构造方法,一个需要proxy以实现拦截,一个需要普通的java类做测试,另一个需要从数据库里面读取以保持集群之间的唯一性,我只需要对不同的环境写不同的工厂,而不需要去改你那个嵌在业务代码中的静态方法,即使改了也不能同时重用于三个场合,你这个时候告诉我这是外部设计的决策. 和你的实现细节无关,我可以在外面去包一个类,那我要你那个静态方法干什么,这个静态方法里面到底是返回proxy还是普通java对象还是从数据库里面读取,任何一个都没有意义 总而言之,任何在本类内部假设自己将被如何、何时、以何种方式进行构造的代码是极其不利于重用的(例如singleton就是一种限制重用的方法,只不过它由他自己适合的环境,因为任何系统里面肯定有一些类是可以重用,而有一些是特定于某个应用的)。因为你适合了一种环境的构造,必然会产生对另一种状况的不适合,所以最好的方式是什么也不假设,把如何构造的责任传递给另外一个类或者框架,那个类可以结合具体的重用场合实现构造,缓存,单件化,动态代理等等它希望做的任何动作。

面向对象的中心点是职责分离和变化频率的分离,对一个希望被重用的对象来说,它的业务代码是它变化频率较低的部分(这是重用的基本假设),而由于目前各种不用容器、测试、分布式计算、事务处理等等场合的需要,它如何被构造的可能性则是一个比它本身业务逻辑变化率高得多的东西,这两种职责必须被分离。

至于你这种设计方法完全依赖于子类和继承上的困难就更不用谈了。你说你的大多数类不让继承,OO最重要的概念就是差异编程和增量编程,这是提高内外部质量、提高生产率的核心思想,如果没有记错的话,《面向对象软件构造》第一章里面就明确地提出了几个重要的内外部指标,也是整个OO思想的软件工程基础。
0 请登录后投票
   发表时间:2004-08-21  
我就知道你们这帮家伙到最后无计可施还是要拿出容器这个大棒子的。即使你们前面说要不谈容器。因为没有容器你们什么都没有。

好吧, 就说容器, 你认为下面这个是个重构天堂:

<factory> 
 <service name="service" class="C"> 
</factory> 


好, 仔细看一下我的逻辑:
1。 容器不支持静态工厂是因为静态厂不好 --- 静态厂不好是因为容器不支持。 搞工程注重实际的人是否在乎在推理里面划圈呢?
2。 pico/spring都支持工厂了, 你还有什么可说的?我不能
<factory> 
 <service name="service" class="C", factorymethod="instance"> 
</factory> 

不行吗?

举个有趣的例子:
个子高于1米7的人抱怨说轮船上的入口太矮, 有碍于高个子的人进入。
个头低于1米七的人说: 高个子的人就不配存在, 因为轮船你们都进不去。
现在,让正常身高的伙计们郁闷的是, 轮船公司无理提高了入口的限制, 高个子的人也进得去了。

正常身高的人们, 现在你们还说什么?别人一伸手就够得到东西真的比你们到哪里都要随身背个梯子要差?
0 请登录后投票
   发表时间:2004-08-21  
引用

1。 容器不支持静态工厂是因为静态厂不好 --- 静态厂不好是因为容器不支持。 搞工程注重实际的人是否在乎在推理里面划圈呢?
2。 pico/spring都支持工厂了, 你还有什么可说的?

我有点转移方向了哦!!!
怎么说着说这就到工厂与容器上了??呵呵,我有点失火了!!!
工厂自然是需要的,不然我也不会多次提到beanfactory了!
现在我们争论的焦点是什么??晕,是不是简单构造器 与 隐藏构造器 的争论????
0 请登录后投票
   发表时间:2004-08-21  
   
  public class X implements I {
...}
      private X (); {
...}
           //blah blah......; 
       }
    
     public static final I instance();{
...}
           I i = lookupJNDI();; 
          if (i == null); {
...}
              i = new X();; 
              registerJNDI(i);; 
          }
          return i; 
      }
  }
  

如果直接用构造函数就是:
 public class X implements I {
...}
      public X (); {
...}
           //blah blah......; 
    
  }

readonly程序里面的用法是
if(i == null);
   i = new X();;
return i;

potian 程序的用法是
          I i = lookupJNDI();; 
          if (i == null); {
...}
              i = new X();; 
              registerJNDI(i);; 
          }
          return i; 
      }

这种做法就是大家都可以用这个X,但却可以用外部代码控制不同的构造方法
0 请登录后投票
   发表时间:2004-08-21  
我就说你们始终都在纠缠者内部构造就不能外部构造这个逻辑错误。我哭!
逻辑的严密性在工程中就真的不重要吗?

这样, 我的静态工厂就是:

class X implements I{
  private X();{...}
  public static I instance();{return new X();;}
}


外部代码就是:

if(i == null); 
    i = X.instance();; 
  return i; 

哪有你们说的问题呀。 你们自己树了靶子打了这么半天。别人怎么叫你们都听不见。

你肯定下面就开始质疑那样静态工厂有什么用, 是么?

我早就强调过了:
静态厂就是封装一下你不需要关心的实现细节。它的目标是让你用起来和用new一样,感觉不出区别。

在这个例子中,如果我改让X的一个内部类实现I, 然后返回这个内部类的实例, 就属于我说的一个对你透明的实现细节。
其它的? 你可以看我最早的论述, 或者看看Bloch的描述。
0 请登录后投票
   发表时间:2004-08-21  
请不要加任何定语,我们在讨论问题
你的意思是不是永远都是这样
 class X implements I{
...}
   private X();{...}
    public static I instance();{return new X();;}
  }

不会在instance()里面放除了new X()以外的其他东西?
是不是这个意思
0 请登录后投票
   发表时间:2004-08-21  
potian 写道
请不要加任何定语,我们在讨论问题
你的意思是不是永远都是这样
 class X implements I{
...}
   private X();{...}
    public static I instance();{return new X();;}
  }

不会在instance()里面放除了new X()以外的其他东西?
是不是这个意思

好。终于引起大家的兴趣来研究我的真正意思是什么了。

不是。我说过了。instance()内部可以放任何东西,只要我可以证明你外部不需要关心。是属于我的实现细节

你的jndi的例子是外部需要关心的,当然不能放在里面。

而我那个对无状态,immutable的singleton的例子, 以及GenericHello的例子,都属于你不需要关心的。
0 请登录后投票
   发表时间:2004-08-21  
引用

静态厂就是封装一下你不需要关心的实现细节。它的目标是让你用起来和用new一样,感觉不出区别。

我不知道你能够在里面放什么东西?如果不是和本程序的特定要求相关,什么东西放在里面才有意义呢?请给个例子?

举你可以任意变化子类的做法,一旦你确定了
class X implements I{
...}
  ...}
     private X();{...}
      public static I instance();{return new X();;}
    }

你已经返回了X的一个实例,下次你在同一个程序内需要返回另一个实现I的Y的类的对象,你必须改变代码

class X implements I{
...}
  ...}
     private X();{...}
      public static I instance();{return new Y();;}
    }

是不是这个意思?
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics