锁定老帖子 主题:基础知识: 需求!
该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2004-08-21
所谓“必须改变代码”。
不知道从合说起? 静态厂的好处是:如果需要改变代码(因为重构), 可以保证隔离变化。 而你说必须“改变代码”, 好像说如果外部需求变化了, 我只能改变代码来应对。 希望我说明白了这两种说法的区别。它们是不同的场景。 另外, 对你这个例子, 在重构时, 只要我能够保证Y具有和X一样的行为,我确实可以这么作。 另外, 关于“工程”,“学院”的东西大家都别扯了,搞得跟朋党之争一样。 我道歉先。 |
|
返回顶楼 | |
发表时间:2004-08-21
需求变化,代码不变是可以做到的,也是我们力图做到的
但我们现在只讨论 引用 我的一个基本论点: 构造函数能达到的灵活性, 静态工厂都达得到. 如果能够推翻这点, 那么, 我的理论自然被推翻. 现在假设potian的程序需要返回X的实例 class X implements I{ ...} ...} private X();{...} public static I instance();{return new X();;} } 而readonly的代码需要返回Y的实例 class X implements I{ ...} ...} private X();{...} public static I instance();{return new Y();;} } 现在这个instance()里面到底写什么呢? |
|
返回顶楼 | |
发表时间:2004-08-21
我在pico和spring出来之前自己也搞过一个东西(weihello和gigix都比较清楚),现在有几个实用的产品都借鉴了我这个东西,但我主要还是利用了proxy和abstract factory.但factory还是问题多多,所以pico一出来我就转了pico。我那个小小的框架里面还有一个自动寻找合适子类的resolver功能,在某些情况下,还算是比这些容器要好一点。
所以我还是蛮希望让大家都了解为什么容器是有意义的,是有助于重用的,这样的讨论也可以让很多人都了解Pico和spring真的不能算是buzz,还是有其产生的非常迫切的背景的。 |
|
返回顶楼 | |
发表时间:2004-08-21
好. 现在我们其实刚刚接触到问题的本质. 这多少有点令人沮丧. 一个让人高兴的方面是, 基本上我们已经砍掉了所有的无关枝节和误会.
现在矛盾的核心是两个: 1. 到底用了静态工厂有什么好处? 2. 用了静态工厂能否保持原来的灵活性. 会否影响外部环境? 我先来回答第一个问题. 假设,有一个需求, 要在sayHello的时候在标准输出打印"x1", 对此, 张三写了一个Hello的实现如下: class X1 implements Hello{ private X1();{} public void sayHello();{System.out.println("x1");;} public static Hello instance();{return new X1();;} } 愚蠢吧? 没办法, 还是一个生造出来的例子. 然后, 用户说, 我还有一个需求, 想在错误输出上打印x2. 现在李四也实现了一个Hello class X2 implements Hello{ private X2();{} public void sayHello();{System.err.println("x2");;} public static Hello instance();{return new X2();;} } 系统编译, 配置,运行, 什么问题都没有. 现在, 公司来了一个牛人, review了一下代码, 把张三,李四纠过来: 你们俩脑袋有包? 怎么做重复工作? 这么简单的代码重用都不会? 看看,我这个类 final class GenericHello implements Hello{ public void sayHello{ stream.println(msg);; } private final String msg; private final XXXStream stream; private GenericHello(XXXStream str, String msg);{ this.stream = str; this.msg = msg; } public static Hello instance(XXXStream str, String msg);{ return new GenericHello(str, msg);; } } 直接调用instance(System.out, "x1"), 和instance(System.err, "x2")不就行了? 现在怎么办? 俩人想了想, 好办. 不用麻烦客户, 客户只关心有两个东西一个打印x1, 一个打印x2, 他们管你怎么打印出来的呢? 通过一个模块的不同参数还是通过两个模块对客户有什么区别? 俩人回去稍微改了一下代码: class X1{ public static Hello instance();{return GenericHello.instance(System.out, "x1");;} } class X2{ public static Hello instance();{return GenericHello.instance(System.err, "x2");;} } bingo. |
|
返回顶楼 | |
发表时间:2004-08-21
不过,如果由配置文件直接来组装组件的话,总给我一种失控的感觉。几十个甚至上百个配置项,自己看着都会吐血。特别像spring这样的,用脚本两行就能明快解决的问题,在这里要敲好多字。
现在我在配置文件里面只放工厂,这样至少保证里面的配置项不会比类个数多。以后我还是希望用groovy来干这个活。 |
|
返回顶楼 | |
发表时间:2004-08-22
potian 写道 需求变化,代码不变是可以做到的,也是我们力图做到的
但我们现在只讨论 引用 我的一个基本论点: 构造函数能达到的灵活性, 静态工厂都达得到. 如果能够推翻这点, 那么, 我的理论自然被推翻. 现在假设potian的程序需要返回X的实例 class X implements I{ ...} ...} private X();{...} public static I instance();{return new X();;} } 而readonly的代码需要返回Y的实例 class X implements I{ ...} ...} private X();{...} public static I instance();{return new Y();;} } 现在这个instance()里面到底写什么呢? 没人说你所有的调用都通过调用X.instance()啊. 你调用X.instance(), Y.instance()不就成了? 就象你分别调用new X(), new Y()一样啊. |
|
返回顶楼 | |
发表时间:2004-08-22
charon 写道 终于搞定崩溃了2天的oa系统,haha.
大家都真有耐心啊,还在搞这个问题。我觉得可以开启思路之处应当都已经差不多了,剩下的,是搞不定伶不清的问题。 我觉得争论这么激烈,大家都没说到对方的点上。我仔细想了一下,按照ajoo的思路,通过factory控制对象产生的方式,这个方法在理论上是可行的,但是如果我们把问题规模扩大一些,就会发现facotory控制对象产生的弊病大大显示出来! 看看这个复杂一点的例子: //先说公开构造器 public interface Service{ public void service();; public Delegater getDelegater();; public void setDelegater(Delegater delegater);; public Object getObject();; public void setObject(Object o);; }; public interface Dalegater{ public Dao getDao();; public void setDao(Dao dao);; public void delegate(Service service);; } public interface Dao{ public void create(Object o);; public void save(Object o);; } public class ServiceImplA implements Service{ private Object object; private Delegater delegater; public ServiceImplA();{ super();; } //getter setter name //getter sertter delegater public void service();{ delegater.delegate(this);; } }; public class DelegaterImplA implements Delegater{ private Dao dao; public DelegaterImplA();{ super();; } //getteer setter for dao public void invocation(Service service);{ dao.save(service.getObject(););; } }; public HibernateDaoImpl implements Dao{ public HibernateDaoImpl();{ //get session and sessionfactoy } // hahahaha } <app> <Bean name="service" interface="Service" class="ServiceImplA"> <property-ref>delegater</property-ref> </Bean> <Bean name="delegater" interface="Delegater" class="DelegagerImpla"> <property-ref>dao</property-ref> </Bean> <Bean name="dao" interface="Dao" class="HibernateDaoImpl"> </Bean> </app> public final class ServiceLoader{ public static Object loadService(String name);{ //读取配置文件信息 //1);找到name为service的bean定义,new Service();出来 //2);发现有属性引用,依次读取属性定义 //3);直到dao的定义,无属性引用new HibenrateDaoImpl(); //4)递归返回,对DelegaterImplA的对象调用setDao //5)d对SerciceImplA对象调用setDelegater(); //组装完毕 return; return result; } } //client端只是使用到Service接口和ServiceLoader: client.java: import ServiceLoader import Service; public class Client { ServiceLoader loader //容器实现 public void init();{ //init for loader } public void doSomething();{ Service service=(service);loader.getService("service");; } }; 上面只是pico或者spring 的一个缩编,假设上面的Impl类构造器被protected在package里。用工厂代理对象产生的方式。 如下 <bean name="serviceA" interface="Service" class="serviceImplA" factoryMethod="instanceForService"> <constructor> <param name="delaterA"></param> </constructor> </bean> <bean name="delegaterA" interface="Delegater" class="DelegateImplA" factoryMethod="instanceForDelegator"> <constructor> <param name="daoA"></param> </constructor> </bean> <bean name="daoA" interface="Dao" class="HibernateDaoImplA" factoryMethod="instanceForDao"> </bean> 从举的例子来看,工厂模式是可以在控制对象产生的方式上来进行动态配置的。但同时这个也带来更多的问题,我们看看配置文件就知道了,protected构造器,意味着factory的实现是多个的,意味着每个包对应一个factory类,还要为获取不同的实现类提供不同的instance方法,为了组装一个服务类,factory需要多少特定的方法??? 假设一个包中的类用到另外一个包的类,而这两个类的构造器是相互看不见的,factory怎么放????再把问题规模放大,多个子系统对应多个包,那么上面的factory怎么配?? 从为了一个控制对象的优点而引入众多的标志,我们看看xml就知道,如果真正到生产环境,xml dtd至少为此庞大上一倍!!!xml dtd定义越复杂,说明实现就越难!!!!! 这是典型的把问题复杂化的案例!!!!为了一个不必要的考虑,而引入如此众多的问题规模,这绝对不可取!!! |
|
返回顶楼 | |
发表时间:2004-08-22
ajoo 写道 俩人回去稍微改了一下代码:
class X1{ public static Hello instance();{return GenericHello.instance(System.out, "x1");;} } class X2{ public static Hello instance();{return GenericHello.instance(System.err, "x2");;} } bingo. hehe, 说到这里我又要扯那个重构的底线了。 实际上,这里的X1和前面最初始的X1已经不是一个东西了。从命名的角度来看,对每一个类我们都会找一个恰当的名字,而在决定这个名字的时候,它所实现的接口会占一个比较重要的因素。 当一个类的接口变化的时候,原来的那个名字已经不适合这个新的功能。 比如,假设原先那个X1被命名为Talker(因为X1这个名字没有实际含义,应用中肯定不会出现),那么,修改接口之后,就Talker类自己实现的功能而言,已经不是Talker,而是皮条客了。 |
|
返回顶楼 | |
发表时间:2004-08-22
charon 写道 ajoo 写道 俩人回去稍微改了一下代码:
class X1{ public static Hello instance();{return GenericHello.instance(System.out, "x1");;} } class X2{ public static Hello instance();{return GenericHello.instance(System.err, "x2");;} } bingo. hehe, 说到这里我又要扯那个重构的底线了。 实际上,这里的X1和前面最初始的X1已经不是一个东西了。从命名的角度来看,对每一个类我们都会找一个恰当的名字,而在决定这个名字的时候,它所实现的接口会占一个比较重要的因素。 当一个类的接口变化的时候,原来的那个名字已经不适合这个新的功能。 比如,假设原先那个X1被命名为Talker(因为X1没有实际含义,应用中肯定不会出现),那么,修改接口之后,就Talker类自己实现的功能而言,已经不是Talker,而是皮条客了。 呵呵. 从具体的到底有什么用扯到比较抽象的什么"实际含义"了? 那么你认同这个作用了? 至于是否改名? 你真要改名没人拦着. 但是你就认为自己永远正确, 自己认为应该改名, 自己认为没有什么实际含义就是圣旨了? 从用户的角度, X1的语义仍然是在标准输出打印"x1", 什么都没有变化. 为什么你的内部实现逻辑变化要影响用户? 你实现者自己认为"我的实际含义不同了", 就可以要求用户改动代码或者配置文件? 我理解的面向接口, 就是你拿到一个接口实例, 只关心它在接口定义上的语义, 而不关心它的实现是个皮条客的adapter, decorator, bridge. |
|
返回顶楼 | |
发表时间:2004-08-22
讨论一个小小的例子是绝对看不出利弊来得!!
怪不得讨论如此僵化,艰难,坎坷,跌宕起伏!!! 我们一起例子规模搞大一点,再来讨论 protected 构造器的利弊!就知道谁对谁错??? |
|
返回顶楼 | |