论坛首页 Java企业应用论坛

基础知识: 需求!

浏览 110170 次
该帖已经被评为精华帖
作者 正文
   发表时间:2004-08-21  
所谓“必须改变代码”。
不知道从合说起?

静态厂的好处是:如果需要改变代码(因为重构), 可以保证隔离变化。

而你说必须“改变代码”, 好像说如果外部需求变化了, 我只能改变代码来应对。

希望我说明白了这两种说法的区别。它们是不同的场景。

另外, 对你这个例子, 在重构时, 只要我能够保证Y具有和X一样的行为,我确实可以这么作。

另外, 关于“工程”,“学院”的东西大家都别扯了,搞得跟朋党之争一样。
我道歉先。
0 请登录后投票
   发表时间: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()里面到底写什么呢?
0 请登录后投票
   发表时间:2004-08-21  
我在pico和spring出来之前自己也搞过一个东西(weihello和gigix都比较清楚),现在有几个实用的产品都借鉴了我这个东西,但我主要还是利用了proxy和abstract factory.但factory还是问题多多,所以pico一出来我就转了pico。我那个小小的框架里面还有一个自动寻找合适子类的resolver功能,在某些情况下,还算是比这些容器要好一点。

所以我还是蛮希望让大家都了解为什么容器是有意义的,是有助于重用的,这样的讨论也可以让很多人都了解Pico和spring真的不能算是buzz,还是有其产生的非常迫切的背景的。
0 请登录后投票
   发表时间: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.
0 请登录后投票
   发表时间:2004-08-21  
不过,如果由配置文件直接来组装组件的话,总给我一种失控的感觉。几十个甚至上百个配置项,自己看着都会吐血。特别像spring这样的,用脚本两行就能明快解决的问题,在这里要敲好多字。
现在我在配置文件里面只放工厂,这样至少保证里面的配置项不会比类个数多。以后我还是希望用groovy来干这个活。
0 请登录后投票
   发表时间: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()一样啊.
0 请登录后投票
   发表时间: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定义越复杂,说明实现就越难!!!!!
这是典型的把问题复杂化的案例!!!!为了一个不必要的考虑,而引入如此众多的问题规模,这绝对不可取!!!
0 请登录后投票
   发表时间: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,而是皮条客了。
0 请登录后投票
   发表时间: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.
0 请登录后投票
   发表时间:2004-08-22  
讨论一个小小的例子是绝对看不出利弊来得!!
怪不得讨论如此僵化,艰难,坎坷,跌宕起伏!!!
我们一起例子规模搞大一点,再来讨论 protected 构造器的利弊!就知道谁对谁错???
0 请登录后投票
论坛首页 Java企业应用版

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