读书人

新手来求教之四_一些设计上的疑惑,该如

发布时间: 2012-04-08 14:38:30 作者: rapoo

新手来求教之四_一些设计上的疑惑
[color=#FF0000][/color]由于经验很少,所以每每遇到程序设计时总是很伤头脑,而最终也往往只能整出个差劲的设计来。
问题1:我设计的常用模块(比如控制台打印、字符集转换过滤、格式化日期、日志文件读写、properties文件读写、javaMail、jsonString读取、excel读取、验证码生成)等等模块经常用私有化构造方法+全部静态化方法来设计这些工具类,这样使用起来就是类名+方法名来使用,请问这种设计方法有何利弊?(tij里曾说过:当你的程序充斥着过多的static时,或许你该想想换个设计了,且实际调用起来越看越像面向过程,虽然用起来时确实方便,不用新建实例)
问题2:我的设计中经常用到抽象类,但却极少用到接口,这正常么?(我的理解是:接口就是抽象类更纯粹的一种表现,但意义是一样的,就是规范了一系列对象的所有动作,使使用这些服务的调用端可以和实际的实现相分离。)
问题3:我为程序中的业务模型层、dao层分别建立了一个工厂,这个工厂以业务模型的名称或代号为参数,以switch\catch的方式去判断并返回所求的实例,但总觉得switch\catch相当臃肿,也觉得,工厂模式除了将对象的创建工作集中到了一处进行统一管理的同时,工厂自己也是够复杂的,尤其是什么工厂方法模式和抽象工厂模式。
-------------------------------------------------------------------------
希望各位前辈能发表下对我这些看法、想法和设计思想的建议和批判,由于程序规模一直不大,且一直没有涉及到过多的修改和扩展,所以无法体会到当前设计是否足够方便扩展和修改。

[解决办法]
接口比抽象类更应该值得取用:
抽象类的优势是子类继承父类方法,这是选择抽象类大于接口的唯一一个说服力原因
至于其他方面,从设计方面讲,接口的优势大于抽象类
[解决办法]
同意楼上

使用接口

关于设计模式

慢慢学习
[解决办法]

探讨
"接口的优势大于抽象类"??这个不太明白
为何我总是使用抽象类,因为用它来表述一系列类似的类时他们总有那么几个动作是完全一样的,可以被直接提取为超类的动作,于是便用了抽象类。。。问题就出在此

[解决办法]
接口可以说是一个标准,抽象类到子类只能说是功能的进一步加强或更改。如果你要做消息传递的话还是得用接口的吧
[解决办法]
楼主多看看开源的工程(HIBERNATE,STRUTS)


帮助会很大的
[解决办法]
使用抽象类的场景:
有些方法写一次即可,后来的子类中不需要重写
然后在真正用的地方,继承你的子类,就可以使用抽象类中的已实现的方法。

而接口,它的方法在实现类中都需重写,它适用于需要不同实现的时候

将共有的方法在抽象类中实现,方法的实现不一样就声明成abstract,子类再去一一重写。抽象类一般用在工具类之类的
而service层一般用接口

现贴出我们项目里非常好的设计,使用抽象类的代码

Java code
public abstract class AbstBaseQuerySpec implements HibernateCallback,        Serializable {    /** 当前页码数 */    protected Paginate paginate = new Paginate();    /** 是否需要分页支持 */    protected boolean paginary = true;    /**     * 重置查询条件     */    public void resetSpec() {        paginate.reset();        reset();    }    public Object doInHibernate(Session session) throws HibernateException,            SQLException {        return null;    }    protected void applyNamedParameterToQuery(Query queryObject,            String paramName, Object value) throws HibernateException {        if (value instanceof Collection) {            queryObject.setParameterList(paramName, (Collection) value);        } else if (value instanceof Object[]) {            queryObject.setParameterList(paramName, (Object[]) value);        } else {            queryObject.setParameter(paramName, value);        }    }    /**     * reset spec     */    protected abstract void reset();    /**     * 执行查询后,通过此方法取得分页信息     *      * @return     */    public Paginate getPaginate() {        return paginate;    }    /**     * @param paginate     *            The paginate to set.     */    public void setPaginate(Paginate paginate) {        this.paginate = paginate;    }    /**     * @param paginary     *            The paginary to set.     */    public void setPaginary(boolean paginary) {        this.paginary = paginary;    }}// 基于Hibernate查询规则限定条件public abstract class AbstQueryStringSpec extends AbstBaseQuerySpec {    /**   */    private Map<String, Object> params = new HashMap<String, Object>();    /**     * Named hql queryName     *      * @return     */    public abstract String queryString();    /**     *      * @param session     * @return     * @throws HibernateException     * @throws SQLException     * @see org.springframework.orm.hibernate3.HibernateCallback#doInHibernate(org.hibernate.Session)     */    public Object doInHibernate(Session session) throws HibernateException,            SQLException {        params.clear();        Query query = session.createQuery(queryString());        Iterator<Map.Entry<String, Object>> pIt = params.entrySet().iterator();        while (pIt.hasNext()) {            Map.Entry<String, Object> entry = pIt.next();            applyNamedParameterToQuery(query, entry.getKey(), entry.getValue());        }        if (paginary) {            List total = query.list();            int totalRows = total.size();            paginate.setTotalRow(totalRows);            query.setFirstResult(paginate.getStartIndex());            query.setMaxResults(paginate.getPageLine());        }        List result = query.list();        return result;    }    /**     * 设置Query接口命名参数     *      * @param name     * @param value     */    protected void setParam(String name, Object value) {        this.params.put(name, value);    }}/** * 功能概述:<br> * 机柜统计查询类 *  *  以下是查询类,具体查询数据时 *  *///public class CabinetStatisticSpec extends AbstQueryStringSpec {    /** 机房ID */    private Integer compLocaId;    @Override    public String queryString() {        // hql 查询        StringBuffer hql = new StringBuffer(                "select cl.name, "                        + "sum(case when nc.useHigh=null or nc.useHigh=0 then 1 else 0 end) as unusedCabinet, "                        + "sum(case when nc.useHigh>0 and nc.useHigh<nc.height then 1 else 0 end) as nofullCabinet, "                        + "sum(case when nc.useHigh>=nc.height then 1 else 0 end) as fullCabinet, "                        + "count(*) as totalCabinet "                        + "FROM NetworkCabinets nc,ComputerLocation cl "                        + "WHERE nc.compLocaId=cl.id ");        if (QuerySpecUtil.valid(compLocaId)) {            hql.append(" AND nc.compLocaId=:compLocaId ");            this.setParam("compLocaId", compLocaId);        }        hql.append(" group by cl.name ");        return hql.toString();    @Override    protected void reset() {        compLocaId = null;    }    public Integer getCompLocaId() {        return compLocaId;    }    public void setCompLocaId(Integer compLocaId) {        this.compLocaId = compLocaId;    }}spec在struts2 action中作为属性,调用service时作为参数,service中直接用DAO查询,参数就是从action中传过来的spec我感觉这个设计还是很好的,遂给出部分代码。希望对大家有一点参考作用 


[解决办法]
问题1:因为你对问题看法是面向过程的,所以你设计类只是为了实现方法。当你以面向对象的观点看待这个世界时,你就会发现..其实这只是两种不同的世界观- -!没有什么所谓的好与不好
问题2:接口是对象的一个方面的表现,这也是面向对象的观点。举例来说:自行车有可以被骑这个特性,所以设计的时候可以这样public class Bicycle implements Ridable,Ridable这个接口从一个侧面描述了自行车。同样也有别的东西可以被骑,虽然骑法不同...这和抽象类的思想就不一样,估计不会有人设计出这样一个抽象类——可以骑的东西...
问题3:没看懂你在问什么- -!
PS:这里能贴UML图么?用语言描述对象是在很累...
[解决办法]
第一个问题。对于纯粹作为方法集的类,全静态方法很少用(不是不能用,只是太怪异,不符合java的语言习惯),一般来说是用单例模式,这个挺简单的,自己搜一搜去。

第二个问题。结合抽象类和接口也可以啊,抽象类首先实现接口,子类再去继承抽象类,OK。

第三个问题。你的工厂模式走歪了。一般来说工厂是为了将获得实例的过程与接收对象彻底分离而设立的,同时还兼有隐藏复杂的初始化过程的作用,比如:

Java code
UserDAO dao = UserDAOFactory.getInstance().createDAO();
[解决办法]
如果觉得自己的设计思路不行的,建议去学下java设计模式
下面是我对你的问题的看法
1: static的类,对象,方法。在你的工程启动的时候他就已经占有了他的内存空间,不管你是否在后面的操作能否有用到。换句话说过多的static类,会加大你的工程启动时的内存占有量。虽然static方法省去了实例的过程以及对象的操作。
2:接口和抽象类,他们的区别只是定义上,接口算得上是一个特殊化的接口(只是里面的所有方法都是虚的),java语言的特点是,一个类只能继承一个抽象类,实现多个接口。所以有了许许多多的接口,不过你开发久了你马上就会觉得接口的设计方式比抽象类要优雅的多。C++中就没有接口,它可以继承多个抽象类
3:建议不要自己去写工厂类,因为正常开发人员的水平都是有限的,考虑不足。用spring去控制你的类对象,你写的工厂类只有控制产生而已,类对象诞生之后,你的工厂类就没有去管对象了,而实际上,工厂类应该是管理类对象整个的生命周期,看看spring的Factory
[解决办法]
我也是新手,我做设计的时候遵循的原则是这样:
1.所有的实现类都必须实现某一特定接口
2.为了提高复用程度,用某一抽象类作为support,实现类可以继承此抽象类

所以最后的格局是:
interface MyInterFace{
}

class abstract MySupport implements MyInterFace{
}

calss ImplementClass extends MySupport{
}

这是我看structs2的app example 时发现的感觉很好用,就一直这样用
[解决办法]
学习一下

抽象类中可以实现方法,因此,如果一个类的某些动作需要按照某种顺序组合,那么就可以用抽象类了,接口做不到
接口定义了类可以实现的动作,不是强制性的(不实现接口),或者表述为:抽象类与子类比接口与实现类之间的关系更紧密

菜鸟一个,仅此浅识
[解决办法]
不一定要切用模式地
[解决办法]
1, 举个例子说 验证码的生成, 如果只有一种生成方法,这种设计 无可厚非, 但是如果存在多种生成办法这样 就不好了,,
过程式设计会用两个static 函数来表示两种不同的验证码生成方法:

public static Code generate1();

public static Code generate2();

如果是面向对象设计,
interface CodeGenerate{
Codge generate();
}

class ConcreteGenerator1 implements CodeGenerate{};
class ConcreteGenerator2 implements CodeGenerate();

这样设计的好处是可扩展性高,符合 O-C原则

2, 接口通常代表一种 职责,或者说某一个方面的功能, 抽象类 经常用于实现一些共有的代码, 面向interface 的编程的好处,同样也是能够做出可扩展性的设计

3, factory 就是为了把一类对象的创造放到factory里面,工厂会因为增加新的对象类型而变化, 而且仅此会导致factory变化, 所以一边 factory不应该有太复杂的代码. 我很少看到switch多得很难维护的情况,顶多10几种,如果多了,,那么就要考虑别的设计策略




[解决办法]
一、 楼上的有人讲过,Static 的东西是程式一启动就会生成一个实例在内存中,如果静态的东西太多,会占用一些内存资源。除此之外,还有一个问题要注意,静态变量的值问题。

三、你的意思是一个工厂,根据一个代号什么的,取出相应的实例。
这样做是对的,但是看起来很庞大。我想这是因为你的实例化对象的方法的问题,一般工厂都会用反射来实例化对象,所有的实例,都只传入名称及参数就可以了。应该不会用到Switch。
[解决办法]
去看看《代码大全》
[解决办法]
静态如果可以用那就用,面向对象未必好,面向过程始终都占有一席之地。
模式不是一层不变,以人为本,应需而变。死忠对象,无异蠢才。
苍茫宇宙,皇者回归。技术之巅,谁与争锋。
[解决办法]
问题1:我设计的常用模块(比如控制台打印、字符集转换过滤、格式化日期、日志文件读写、properties文件读写、javaMail、jsonString读取、excel读取、验证码生成)等等模块经常用私有化构造方法+全部静态化方法来设计这些工具类,这样使用起来就是类名+方法名来使用,请问这种设计方法有何利弊?(tij里曾说过:当你的程序充斥着过多的static时,或许你该想想换个设计了,且实际调用起来越看越像面向过程,虽然用起来时确实方便,不用新建实例)
部分静态方法还是可以的,比如:格式化日期、properties文件读写,控制台打印,字符集转换过滤等等。
不过大量使用静态方法确实是设计不够优化的表现,尤其是新手,经常会碰到一段代码不知道怎么才能大家共享调用,于是就静态之。


这个说到底还是要深入理解OO,理解扩展性、耦合性等非功能因素。
举例来说:字符集转换,
你可以写静态方法,Util.convertCharSet(String content,String from,String to);
也可以:
CharSetConverter cc=new CharSetConverter(from, to);
cc.convert(content);
如果你有大量的字符串需要转换的时候,显然后者的代码更加容易理解,因为不是每次都要带着from,to两个参数,
你可以说不要这两个参数(从哪种字符集转到另外一种字符集),那你的代码就是固定功能,没有扩展性的。

总体来讲,如果你确定这些东西都是工具,不涉及业务逻辑,用静态方法,我觉得没什么,方便很重要。




问题2:我的设计中经常用到抽象类,但却极少用到接口,这正常么?(我的理解是:接口就是抽象类更纯粹的一种表现,但意义是一样的,就是规范了一系列对象的所有动作,使使用这些服务的调用端可以和实际的实现相分离。)
正是接口的更纯粹抽象使得接口与抽象类不一样,而且,在模块之间,或者你对外提供API的时候,开放接口和开放抽象类是完全不同的。
作为你的API的使用者,如果你要别人提供的数据继承自你的抽象类,会有很大的制约性(因为人家可能已经继承自他自己的某个类了)。
所以,面向接口编程,就是为了完全的将合作双方隔离。



问题3:我为程序中的业务模型层、dao层分别建立了一个工厂,这个工厂以业务模型的名称或代号为参数,以switch\catch的方式去判断并返回所求的实例,但总觉得switch\catch相当臃肿,也觉得,工厂模式除了将对象的创建工作集中到了一处进行统一管理的同时,工厂自己也是够复杂的,尤其是什么工厂方法模式和抽象工厂模式。
工厂模式,可以使用反射机制来消除if switch。
当然,前提是你的类命名有一定的规范(与编号之间)。

[解决办法]
关于抽象类和接口,偶举个例子,门有木门和铁门,这个可以用抽象类和具体类来实现,但是现在要加上报警的功能就要用到接口了, java的jdk设计很多都是这种思想,有空可以看看它的类图

读书人网 >J2EE开发

热点推荐