智能领域对象设计

10-07-04 cuwkuhaihong
我们重新审视的以下JAVA的Object的对象,其中包含了两个特殊的方法,equals和clone,从这两个方法我们可以看出,在面向对象的程序设计中,对象的设计不能紧紧参考其所描述的对象,而不是不可添加其他便于对象活动的行为的。为什么会有这样的结论,equals的判断两个对象是否相同,这种对象与对象之间相同与否的问题,是只有具备智能的动物或者智能机器人才有的能力做这样的结论,大多数的对象是没有此能力的,clone就不必说了。故做对象设计不必严格遵守现实,现在大家在写程序的时候经常创建的javabean,只有setter和getter,也是一种无活力对象,只具备传递数据的能力,而且要配合对应的service方法才完成工作,service类就是一个方法集,严格来讲不是面向对象编程。

我们不必在意是否遵守了面向对象的设计方法,能提高生产力的方法,就是好方法。我尝试了一种新的设计方法,看是否可以提高生产力,大家不妨看看。

第一,Object对象已经具备equals和clone了这样的智能方法了,我们不妨再让这个object聪明点,给他提供记忆联想功能。接下来的两点详细说明。

第二,很多对象是需要存储的,对象储取能力不需要外部提供,而是继承提供存取能力的对象,这就好像说,你家养的狗狗可以自己到宠物管理机构登记和变更它自己的信息了(听着都轻松),不用那么现实。

第三,世界上任何对象都不是孤立存在,而是相互影响的,存在因果关系的,一种原因能产生什么样的结果,有些是一定的,有的却不一定,对象之间的关联,有些情况是联系紧密的、有些是疏散的,对象具备自己的关系网络。如你目睹一起三车相撞的交通事故,其中一人的腿的伤势给你印象深刻,以后任何人提起腿受伤,你都会想起那个人,接着想起亲眼目睹那次事故,你还可以想到这起事故具体是如何发生的,即有点到面,有面到体,逐步深入。下面是我的一个对象设计(源代码https://sourceforge.net/projects/thinery/files/try.zip/download

):


public abstract class IObject extends Observable {
	protected static boolean inited = false;
	
	public IObject() {
		….....
		
	}
	
	/**
	 * 对象第一此加载的时候初始化所有的监听器,该类的活动如果影响的其他对象
	 * 都可以在监听器完成因果传递,监听器统一配置在一个叫:[子类名称]_lstnr.properties
	 * 的配置文件中,如果对象找不到该文件,会认为没有监听器
	 */
	static Map<String, Observer> observers = new HashMap();

	/**
	 * 把对象自身持久的数据库的方法。子类可以根据类的情况,重写该方法。 如该类是一对一,
         *一对多的,想把关联对象一起存储,就需要重写该方法
	 */
	public abstract void remember();

	/**
	 * 删除此对象对应的数据库记录。 恰如我们忘记一件事物
	 */
	public abstract void forget();

	/**
	 * 用于更新数据库记录,需要更新对象的某一个属性时,用到此方法。 恰如我们需要更新对一件事物的记忆。
	 * 我们每隔三天去看一个正在建设的高楼,那么大脑每次都要更新高楼的高度。
	 * 
	 * @param updateFields
	 *            属性的名称,如果你更改username的值,那么这个参数就应该是username, 注意不是username的值
	 */
	public abstract void updateMemory(String... updateFields);

	/**
	 * 这是一个最自由的方法,该类方法名称意为”联想“,一般默认实现是查询相关对象。 
         * 如:我们通过宝马,联想到奔驰和奥迪。
	 * 如果这是一个事件对象,那么你可以让他想到与这个事件相关的任何东西。
	 * 
	 * @param params
	 *            任何对象
	 * @return
	 */
	public abstract List associate(Object... params);

	/**
	 * 根据主键数据库中获取自身的全部信息
	 * 
	 * @param id
	 * @return
	 */
	public abstract void obtainMe();
}
<p>

6
cuwkuhaihong
2010-07-04 19:59
我基于Thin完成一个实现,Thin是一种基于key-value的持久层框架,简单易用,体现持久本质。在此不再详述。大家可以参考源代码:http://sourceforge.net/projects/thinery/

public class ThinObject extends IObject {
	protected static Logger log = Logger.getLogger(IObject.class);
	static {
		/**
		 * 这里建立了一种约定,一种对象的属性名与数据库中表字段名相互转换的约定。
		 */
		KVUtils.mappingRule = new AnnotationMappingRule();
	}

	/**
	 * 在配置文件中加载自定义的SQL,存放在此处以供该类的方法使用
	 * 
	 */
	static Properties sqls = new Properties();

	transient protected BeanTable beanTable;

	/**
	 * 把该类的所有属性,全部转化成key-value用于存储
	 * 
	 * @return
	 * @throws Exception
	 */
	protected Map<String, Object> toKeyValue() throws Exception {
		return KVUtils.toKeyValue(this);
	}

	protected Field hasPrimary() {
		Field[] fields = getClass().getDeclaredFields();
		for (Field field : fields) {
			Primary pry = field.getAnnotation(Primary.class);
			if (pry != null) {
				return field;
			}
		}
		return null;
	}

	/**
	 * 该设计的持久方式采用的是'thin',<a
	 * href="http://sourceforge.net/projects/thinery/">thin</a>
	 * 是基于key-value的持久层框架,简单,易用,透析持久本质; 默认采用约定优先,注解在后的映射方式,还支持定义映射。
	 * 
	 * @return
	 * @throws SQLException
	 */
	protected BeanTable getBeanTable() throws SQLException {

		if (beanTable == null) {
			BeanTableName btn = this.getClass().getAnnotation(
					BeanTableName.class);
			String tableName = btn.name();
			String schema = btn.schema();
			if ("".endsWith(schema)) {
				beanTable = ThinContext.getThinContext()
						.getBeanTable(tableName);
			} else {
				beanTable = ThinContext.getThinContext(schema).getBeanTable(
						tableName);
			}
		}

		return beanTable;

	}

	public ThinObject() {
		if (!inited) {
			try {
				initSQLProperties();
			} catch (Exception e) {
				e.printStackTrace();
			}
			inited = true;
		}

	}

	private void initSQLProperties() throws IOException {
		InputStream inStream = this.getClass().getClassLoader()
				.getResourceAsStream(this.getThisName() + "_sql.properties");
		if (inStream != null) {
			sqls.load(inStream);
		}
	}

	@SuppressWarnings("unchecked")
	protected List toBeans(List<Map<String, Object>> keyvalues)
			throws Exception {
		return KVUtils.toBeans(keyvalues, this.getClass());
	}

	/**
	 * 把对象自身持久的数据库的方法。子类可以根据类的情况,重写该方法。 如该类是一对一,一对多的,想把关联对象一起存储,就需要重写该方法
	 * 
	 * @throws RuntimeException
	 */
	public void remember() throws RuntimeException {
		try {
			this.getBeanTable().addOrUpdate(toKeyValue());

			this.setChanged();
			this.notifyObservers();
		} catch (Exception e) {
			log.error("记忆出现异常!", e);
			throw new RuntimeException(e);

		}
	}

	/**
	 * 删除此对象对应的数据库记录。 恰如我们忘记一件事物
	 */
	public void forget() {
		try {
			this.getBeanTable().delete(toKeyValue());
			
			this.setChanged();
			this.notifyObservers();
		} catch (Exception e) {
			log.error("删除记录出现异常", e);
			throw new RuntimeException(e);
		}

	}

	public void updateMemory(String... updateFields) {
		try {
			Map<String, Object> keyvalues = new HashMap<String, Object>();
			for (String name : updateFields) {
				KVUtils.mappingRule.setDBColumn(this, this.getClass()
						.getDeclaredField(name), keyvalues);
			}

			Field primaryField = hasPrimary();
			if (primaryField != null
					&& !keyvalues.containsKey(primaryField.getName())) {
				KVUtils.mappingRule.setDBColumn(this, primaryField, keyvalues);
			}

			this.getBeanTable().update(keyvalues);

			this.setChanged();
			this.notifyObservers();
		} catch (Exception e) {
			log.error("更新存储出现异常", e);
			throw new RuntimeException(e);
		}
	}

	@SuppressWarnings("unchecked")
	public List<Object> associate(Object... params) {
		try {
			Criterion[] criterions=toCriterion(params);
			
			return this.toBeans(this.getBeanTable().get(criterions));
		} catch (Exception e) {
			log.error("查询记录出现异常", e);
			throw new RuntimeException(e);
		}
	}

	private Criterion[] toCriterion(Object... params) {
		Criterion[] criterions = new Criterion[params.length];
		for(int i=0;i<params.length;i++){
			criterions[i] = (Criterion)params[i];
		}
		return criterions;
	}

	public void obtainMe() {
		try {
			Field field = this.hasPrimary();
			Object value = PropertyUtils.getProperty(this, field.getName());

			List<Map<String, Object>> keyvalues = this.getBeanTable().get(SQLCriterion.get(field.getName(), value));
			if(!keyvalues.isEmpty()){
				KVUtils.toBean(keyvalues.get(0), this);
			}

		} catch (Exception e) {
			log.error("查询记录出现异常", e);
			throw new RuntimeException(e);
		}
	}

}
<p>

在使用的时候只需要继承此对象,增删改查的功能就不用在写,子类可以增加新方法,也可以覆盖夫类的方法。未来的社会的智能的社会,智能的社会需要智能的工具,智能工具需要智能的、功能可独立的软件,这种软件将最终落在智能对象设计上。

cuwkuhaihong
2010-07-04 22:18
一个对象User的增删改查的实现

@BeanTableName(name="z_user")
public class User extends ThinObject {

	@Primary
	String uid ;
	
	String uname;
	String rid;
	
	public User(String userid, String username) {
		super();
		this.uid = userid;
		this.uname = username;
	}
	
	public User() {
	}
	@Override
	public String toString() {
		return "(userid:"+uid+", username:"+uname+")";
	}

	public String getUid() {
		return uid;
	}

	public void setUid(String uid) {
		this.uid = uid;
	}

	public String getUname() {
		return uname;
	}

	public void setUname(String uname) {
		this.uname = uname;
	}

	public String getRid() {
		return rid;
	}
	public void setRid(String rid) {
		this.rid = rid;
	}
}
<p>

测试类:

public class TestUser extends TestCase {

	static {
		new TryThinContext("zero");
		ThinContext.setDefaultSchema("zero");
	}
	
	public void testUpdate() throws SQLException{
		User user = new User("wanghh","洪");
		user.obtainMe();
		user.setUname("测试");
		user.updateMemory("uname");
		System.out.println(user.rid);
		
	}
	
	public void testAdd() throws SQLException{
		User user = new User("thinery","洪");
		user.setRid("admin");
		user.remember();
	}
	
	public void testDelete() throws SQLException{
		User user = new User();
		user.setUid("thinery");
		user.forget();
	}
	
	public void testQuery() throws SQLException{
		User user = new User();
		List<Object> users = user.associate(SQLCriterion.get("rid", Operator.EQ, "marketing"));
		System.out.println(users);
		
	}
	
	@Override
	protected void tearDown() throws Exception {
		ThinContext.ctx.cleanHoldConection();
	}
}
<p>

[该贴被cuwkuhaihong于2010-07-04 22:19修改过]

[该贴被cuwkuhaihong于2010-07-04 22:20修改过]

oojdon
2010-07-05 12:53
很有想法的楼主,不错,加油。

我正在研究你的代码,呵呵!

banq
2010-07-05 19:03
2010年07月04日 19:57 "cuwkuhaihong"的内容
这种对象与对象之间相同与否的问题,是只有具备智能的动物或者智能机器人才有的能力做这样的结论,大多数的对象是没有此能力的 ...

我认可这个观点,我们在建立Object,要注意它是一个智能,能够知道什么,决定做什么,自己做决定,职责驱动开发中讲得比较多。

这应该是一种主流的对象建模方法。

tianhaoleng
2010-07-06 13:34
我晕,别人说啥BANQ就同意啥……

衡量设计的标准不是人的想象力,而是你的设计与现实的契合程度。

“让狗狗自己去机构登记”……哥哥对你无语。

假设人扫地,你是用自己的手掌在地上磨呢,还是利用工具呢?

把工具理解成Service怎么样?

cuwkuhaihong
2010-07-06 19:28
2010年07月06日 13:34 "tianhaoleng"的内容
衡量设计的标准不是人的想象力,而是你的设计与现实的契合程度。 ...

如果完全考虑契程度的话,Object 的equals()和clone()本身就是不契合现实的,若用契合度衡量对象设计,那Object的诞生就是一个错误。开篇就说了

cuwkuhaihong
2010-07-06 22:11
对疑问的答复,来自其他站点

Service + DAO 的确不是面向对象的方法,但为什么它那么有生命力,乃至有人把它命名成贫血模型,Domain Object 的想法很好,为什么却一直没有得到大规模应用?这其中有着很复杂的原因,希望你多考虑一下。

我没太明白你的智能领域对象的智能二字体现在那里,是指Thin Object对持久化的支持吗?其他领域对象都通过继承来获取这种智能,是不是也是一种侵入呢?我如果继承其它对象怎么办?

我的一点建议,请你参考。

答:

service+DAO为什么具有生命力而Domain Object为什么没有得到广泛的应用,我说不全面,需要你赐教,编程习惯是一个很重要的因素,观念的变革也总是曲折前进嘛。至于有人将他命名为“贫血模型”,我并不认为这是对是这种编程模式的赞扬,换个角度讲这并是什么值得研究的问题,发明自行车的人不知道自己为什么可以走路。我更专注的是如何提高生产力,抹去重复无聊的代码。

至于“智能”二字如何体现,确实不容易说清楚,但是和thin没有什么关系。具备独立思考和判断能力的个体即为智能个体,我是希望让所有继承IObject的对象都尽量独立处理自己的份内事物,而非完全寄托于那个专门为该对象而写的service,IObject有个模糊的方法叫“联想”,用于模拟大脑的联想方式,通过一个对象联想到与其相关的事物,这是符合大脑思维方式的,如果觉得这种解释牵强或者模糊不清,那就稍等我知识丰富了再回答一次。

ThinObject只是这种编程方式的一种实现,thin是不错的选择,不喜欢也可以使用别。当然继承ThinObject显然是一种入侵,只是用“入侵”这个词不好,任何行为都具有两面性,要看它好与坏哪个占的多,所以我更喜欢用一个中性词--“介入”。有的对象在处理业务逻辑时需要与数据库进行交互那就可以继承ThinObject,否则可以不用继承,并没有强制到不继承ThinObject,java对象就不能使用程度,更不是说有了ThinObject就彻底消灭了service,而是当你需要一个具有储存能力的对象时ThinObject的介入能给你提供良好的解决方案,并不带来任何不利的影响。举个不恰当的例子:中国改革开发需要国外的技术和资金,不能因担心他们二次入侵中国而将其拒之门外,我们清楚自己需要的是什么,无法提供这些东西我们是不需要他们的。

已经继承了其他类,要么不继承ThinObject,要么让顶级父类继承。

banq
2010-07-07 11:05
2010年07月06日 13:34 "tianhaoleng"的内容
假设人扫地,你是用自己的手掌在地上磨呢,还是利用工具呢? ...

这个问题很有代表性,首先是利用工具,但是对工具使用的决定,以及工具使用的策略都是人这个对象的职责。

当你使用工具扫地时,这中间有两个细腻的操作:领域对象的职责:负责决定和使用策略,控制作用,主要负责“做什么”问题;协作:通过委托工具来最终完成扫地,主要实现“怎么做”问题。

见:

如何从职责和协作中发现丰富对象?

所以,我认为他这里智能领域对象,也就是在领域对象中要封装做什么,是什么等战略性目标性的业务知识。

过去,我们将“做什么”和“怎么做”混同在一起,这已经成为很多人思考习惯,而在一个复杂大型项目,必然对其要进行分离,DSL也是这种分离的象征。

将“做什么”和“怎么做”混同在一起最主要表现就是Service+DAO方式,将功能一股脑混在Service提供了,而不是对这些功能操作进行“做什么”和“怎么做”细腻度划分,做什么 应该属于领域对象的代码,在领域对象中实现,这才是富模型充血模型,而如果你将这两者混合在Service中实现了,当然领域对象就是失血模型了。

SOA只是细化到服务Service这一层,对Service内部应该如何构造没有说明,因为SOA主要提供一种多角度视图让业务市场人员和技术人员能够看同一种事物,Service内部属于纯技术活,SOA就管不到了,没法找到市场客户人员也能理解的对应概念,而且作为软件使用者,只需要知道你的服务Service能提供什么功能就可以,至于服务内部是否进行“做什么”“怎么做”分离,则SOA不会管,这部分是OO设计的事情。

[该贴被banq于2010-07-07 11:18修改过]

cuwkuhaihong
2010-07-08 19:37
banq是位好老师,总能不厌其烦的解答问题。

supernavy
2011-01-13 16:46
希望不要把智能和多功能2个概念搞混了。智能是自己可以根据内部的一些决策自动的决定去做什么,而不是说有能力去做什么。而且所谓的智能也是相对,没有绝对的智能和不智能,你觉得人够智能吗?为什么还有那么多人被老板骂,“这还用我说?”。当然也有人被人骂,“你真多事”,“你脑子进水了”。 所以其实智不智能永远是次要的,如果因为所谓的智能导致功能的风险大于收益,这样的智能宁愿不要。

就拿这个remember来说,乍一下觉得有点道理,仔细琢磨一下就觉得非常的不合理了。这根本不是智能,只是多了个功能而已。人是个对象,请问你会跟自己说把看到的东西记下来吗?根本不存在你发出remember这样的指令,这才叫做智能。有时候你看到的时候就已经自动的记下来了,而有时候看到了也没记,这个决定记不记的过程才是智能的体现。因此楼主所谓的智能其实还是停留在职责分配的层面上,只不过他觉得这样分配符合他的喜好而已。我也希望人能都有fly()的方法,事实上我希望人有的功能越多越好,最好能有个earnMoney()的方法。也许楼主觉得大家都公认人都应该有remember()这个方法,但是我相信每个人对remember()这个方法的理解和应该做的事情,绝对无法统一,所以也绝对无法从jdk的这个层面上提供一个默认的实现。equals和clone则不一样,他们的概念非常非常的简单,可以提供一个被大多数人接受的默认的实现。如果非要给Object加功能,我觉得compare应该是下一个相对容易提供被大多数人接受的默认实现的方法。

[该贴被supernavy于2011-01-13 16:57修改过]

猜你喜欢