??xml version="1.0" encoding="utf-8" standalone="yes"?> 讉K地址Q?a >http://www.openldap.org/jldap/ q下载相关lib 讉K地址Q?a >http://www.openldap.org/jdbcldap/ q下载相关lib 原创人员QNicholas
上面主要从语法定义和~程的角度论qCabstract class和interface的区别,q些层面的区别是比较低层ơ的、非本质的。本节从另一个层面:abstract class和interface所反映出的设计理念Q来分析一下二者的区别。作者认为,从这个层面进行分析才能理解二者概늚本质所在?
前面已经提到q,abstarct class在Java语言中体C一U承关p,要想使得l承关系合理Q父cdzcM间必d?is a"关系Q即父类和派生类在概忉|质上应该是相同的Q参考文献?〕中有关?is a"关系的大幅深入的论qͼ有兴的读者可以参考)。对于interface 来说则不Ӟq不要求interface的实现者和interface定义在概忉|质上是一致的Q仅仅是实现了interface定义的契U而已。ؓ了便于理解Q下面将通过一个简单的实例q行说明?
考虑q样一个例子,假设在我们的问题领域中有一个关于Door的抽象概念,该Doorh执行两个动作open和closeQ此时我们可以通过abstract class或者interface来定义一个表C抽象概念的类型,定义方式分别如下所C:
使用abstract class方式定义DoorQ?
abstract class Door {
abstract void open();
abstract void close()Q?
}
使用interface方式定义DoorQ?
interface Door {
void open();
void close();
}
其他具体的Doorcd可以extends使用abstract class方式定义的Door或者implements使用interface方式定义的Door。看h好像使用abstract class和interface没有大的区别?
如果现在要求Doorq要h报警的功能。我们该如何设计针对该例子的cȝ构呢Q在本例中,主要是ؓ了展Cabstract class和interface反映在设计理念上的区别,其他斚w无关的问题都做了化或者忽略)Q下面将|列出可能的解决Ҏ(gu)Qƈ从设计理念层面对q些不同的方案进行分析?
解决Ҏ(gu)一Q?
单的在Door的定义中增加一个alarmҎ(gu)Q如下:
abstract class Door {
abstract void open();
abstract void close()Q?
abstract void alarm();
}
或?
interface Door {
void open();
void close();
void alarm();
}
那么h报警功能的AlarmDoor的定义方式如下:
class AlarmDoor extends Door {
void open() { … }
void close() { … }
void alarm() { … }
}
或?
class AlarmDoor implements Door ?
void open() { … }
void close() { … }
void alarm() { … }
?
q种Ҏ(gu)q反了面向对象设计中的一个核心原则ISPQInterface Segregation PricipleQ,在Door的定义中把Door概念本n固有的行为方法和另外一个概?报警?的行为方法在了一赗这样引L(fng)一个问题是那些仅仅依赖于Doorq个概念的模块会因ؓ"报警?q个概念的改变(比如Q修改alarmҎ(gu)的参敎ͼ而改变,反之依然?
解决Ҏ(gu)二:
既然open、close和alarm属于两个不同的概念,Ҏ(gu)ISP原则应该把它们分别定义在代表q两个概늚抽象cM。定义方式有Q这两个概念都用abstract class方式定义Q两个概念都使用interface方式定义Q一个概念用abstract class方式定义Q另一个概念用interface方式定义?
昄Q由于Java语言不支持多重承,所以两个概念都使用abstract class方式定义是不可行的。后面两U方式都是可行的Q但是对于它们的选择却反映出对于问题领域中的概念本质的理解、对于设计意囄反映是否正确、合理。我们一一来分析、说明?
如果两个概念都用interface方式来定义,那么反映出两个问题Q?、我们可能没有理解清楚问题领域,AlarmDoor在概忉|质上到底是Doorq是报警器?2、如果我们对于问题领域的理解没有问题Q比如:我们通过对于问题领域的分析发现AlarmDoor在概忉|质上和Door是一致的Q那么我们在实现时就没有能够正确的揭C我们的设计意图Q因为在q两个概늚定义上(均用interface方式定义Q反映不Zq含义?
如果我们对于问题领域的理解是QAlarmDoor在概忉|质上是DoorQ同时它有具有报警的功能。我们该如何来设计、实现来明确的反映出我们的意思呢Q前面已l说q,abstract class在Java语言中表CZU承关p,而承关pd本质上是"is a"关系。所以对于Doorq个概念Q我们应该用abstarct class方式来定义。另外,AlarmDoor又具有报警功能,说明它又能够完成报警概念中定义的行ؓQ所以报警概念可以通过interface方式定义。如下所C:
abstract class Door {
abstract void open();
abstract void close()Q?
}
interface Alarm {
void alarm();
}
class AlarmDoor extends Door implements Alarm {
void open() { … }
void close() { … }
void alarm() { … }
}
q种实现方式基本上能够明的反映出我们对于问题领域的理解Q正的揭示我们的设计意图。其实abstract class表示的是"is a"关系Qinterface表示的是"like a"关系Q大家在选择时可以作Z个依据,当然q是建立在对问题领域的理解上的,比如Q如果我们认为AlarmDoor在概忉|质上是报警器Q同时又hDoor的功能,那么上述的定义方式就要反q来了?
转蝲人员-Nicholas
package cn.myapps.test;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
public class LdapTest {
public void JNDILookup() {
String root = "o=teemlink,c=cn";
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://192.168.0.30/" + root);
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, "cn=Nicholas,ou=develop,o=teemlink,c=cn");
env.put(Context.SECURITY_CREDENTIALS, "123456");
DirContext ctx = null;
try {
ctx = new InitialDirContext(env);
Attributes attrs = ctx.getAttributes("cn=Nicholas,ou=develop");
System.out.println("Last Name: " + attrs.get("sn").get());
System.out.println("认证成功");
} catch (javax.naming.AuthenticationException e) {
e.printStackTrace();
System.out.println("认证p|");
} catch (Exception e) {
System.out.println("认证出错Q?/span>");
e.printStackTrace();
}
if (ctx != null) {
try {
ctx.close();
} catch (NamingException e) {
// ignore
}
}
}
public static void main(String[] args) {
LdapTest LDAPTest = new LdapTest();
LDAPTest.JNDILookup();
}
}
5.2 用JLDAPq访?/h2>
import java.io.UnsupportedEncodingException;
public class List
{
public static void main(String[] args)
{
int ldapPort = LDAPConnection.DEFAULT_PORT;
int searchScope = LDAPConnection.SCOPE_ONE;
int ldapVersion = LDAPConnection.LDAP_V3;
boolean attributeOnly = false;
String attrs[] = null;
String ldapHost = "192.168.0.30";
String loginDN = "cn=Manager,o=teemlink,c=cn";
String password = "secret";
String searchBase = "ou=develop,o=teemlink,c=cn";
String searchFilter = "objectClass=*";
LDAPConnection lc = new LDAPConnection();
try {
// connect to the server
lc.connect(ldapHost, ldapPort);
// bind to the server
lc.bind(ldapVersion, loginDN, password.getBytes("UTF8"));
LDAPSearchResults searchResults =
lc.search(searchBase, // container to search
searchScope, // search scope
searchFilter, // search filter
attrs, // "1.1" returns entry name only
attributeOnly); // no attributes are returned
// print out all the objects
while (searchResults.hasMore()) {
LDAPEntry nextEntry = null;
try {
nextEntry = searchResults.next();
System.out.println("\n" + nextEntry.getDN());
System.out.println(nextEntry.getAttributeSet());
} catch (LDAPException e) {
System.out.println("Error: " + e.toString());
// Exception is thrown, go for next entry
continue;
}
}
// disconnect with the server
lc.disconnect();
} catch (LDAPException e) {
System.out.println("Error: " + e.toString());
} catch (UnsupportedEncodingException e) {
System.out.println("Error: " + e.toString());
}
System.exit(0);
}
}5.3 用JDBC-LDAPq访?/h2>
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class JdbcLdap {
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
Class.forName("com.octetstring.jdbcLdap.sql.JdbcLdapDriver");
String ldapConnectString = "jdbc:ldap://192.168.0.30/o=teemlink,c=cn?SEARCH_SCOPE:=subTreeScope";
Connection con = DriverManager.getConnection(ldapConnectString, "cn=Manager,o=teemlink,c=cn", "secret");
String sql = "SELECT * FROM ou=develop,o=teemlink,c=cn";
Statement sat = con.createStatement();
ResultSet rs = sta.executeQuery(sql);
while (rs.next()) {
System.out.println(rs.getString(1));
}
if (con != null)
con.close();
}
}
文章来源:http://www.cnblogs.com/obpm/archive/2010/08/28/1811065.html
]]>
Replace Type Code with Subclasses
对Y件内部结构的㆒种调整Q目的是在不改变「Y件之可察行ؓ」前提下Q提高其可理解性,降低其修Ҏ(gu)本?/span>
使用一pd重构准则Q手法)Q在不改变「Y件之可察行ؓ」前提下Q调整其l构?/span>
同样完成一件事Q设计不良的E序往往需要更多代码,q常常是因ؓ代码在不同的?/b>方用完全相同的语句做同L(fng)?/b>。因此改q设计的一个重要方向就是消除重复代?b>QDuplicate CodeQ?/span>
你的源码q有其它读者:C月之后可能会有另一位程序员试L你的代码q做一些修?/b>。我们很Ҏ(gu)忘记q第二位读者,但他才是最重要的。计器是否多花了数个钟头进行编译,又有什么关pdQ如果一个程序员p一周时间来修改某段代码Q那才关p重?mdash; 如果他理解你的代码,q个修改原本只需一时
Kent Beck l常形容自己的㆒句话Q?b>我不是个伟大的程序员Q我只是个有着?/b>些优U?fn)惯的好E序员而已。』重构能够帮助我更有效地写出强固E_QrobustQ的代码?b>
l于Q前面的一切都归结Cq最后一点:重构帮助你更快速地开发程序。听h有违反直觉。当我谈到重构,Z很容易看出它能够提高质量?b>改善设计、提升可L、减错?/b>Q这些都是提高质量。但q难道不会降低开发速度吗?我强烈相信:良好设计是快速Y件开发的Ҏ(gu)。事实上拥有良好设计才可能达成快速的开发?b>如果没有良好设计Q或许某一D|间内你的q展q速,但恶劣的设计很快p你的速度?/b>?/b>来。你会把旉花在调试?/b>面,无法d新功?/b>。修Ҏ(gu)间愈来愈长,因ؓ你必花愈来愈多的时间去理解pȝ、寻N复代码。随着你给最初程序打上一个又一个的补丁QpatchQ,新特性需要更多代码才能实现。真是个恶性@环?
重构本来׃是一件「特别拨出时间做」的事情Q重构应该随旉地进?/span>。你不应该ؓ重构而重构,你之所以重构,是因Z惛_别的什么事Q而重构可以帮助你把那些事做好
Don Roberts l了我一条准则:W一ơ做某g事时只管dQ第二次做类似的事会产生反感Q但无论如何q是做了Q第三次再做cM的事Q你应该重构?
?nbsp;事不q?/b>?/b>Q?/b>?/b>则重构。(Three strikes and you refactor.Q?/b>
d功能时一q?/span>
修补错误时一q?/span>
复审代码时一q?
-以上章节摘抄自《重?改善既有代码的设?b>?/b>
1. 实例模块为ViewQ重构元素ؓViewAction、ViewProcessBean、ViewQ以下ؓ关系图?/span>
2. ׃View存在多种editModeQ编辑模式)Q而每个调用的地方都需要进行type codeQ类型码Q判?/b>Q然后再q行相应的业务逻辑处理Q最l在每个调用的地斚w形成了大量的if-else代码Q大大减׃代码的可L,和逻辑清晰度?/span>
3. 调用的地方:
如上图所C,每Utype code重构成subclassQ加Z每种cd处理业务逻辑的能力?/span>
View-版本1566代码片段Q?/span>
public EditMode getEditModeType() {
if (EDIT_MODE_CODE_DQL.equals(getEditMode())) {
return new DQLEditMode(this);
} else if (EDIT_MODE_CODE_SQL.equals(getEditMode())) {
return new SQLEditMode(this);
} else if (EDIT_MODE_DESIGN.equals(getEditMode())) {
return new DesignEditMode(this);
}
return new NullEditMode(this);
}
说明Q调用者无需了解具体的类型,由View自n作判断,q回EditMode接口Q从而实现多态调用?/span>
ViewProcessBean代码片段Q?/span>
重构?版本1503Q?/span>
public String expDocToExcel(String viewid, WebUser user, ParamsTable params) throws Exception {
if (view.getEditMode().equals(View.EDIT_MODE_DESIGN)) {
datas = dp.queryBySQLPage(sql, params, tempPage, LINES, user.getDomainid());
} else if (view.getEditMode().equals(View.EDIT_MODE_CODE_DQL)) {
datas = dp.queryByDQLPage(dql, params, tempPage, LINES, user.getDomainid());
} else if (view.getEditMode().endsWith(View.EDIT_MODE_CODE_SQL)) {
datas = dp.queryBySQLPage(sql, params, tempPage, LINES, user.getDomainid());
}
}
重构?版本1566Q?/span>
public String expDocToExcel(String viewid, WebUser user, ParamsTable params) throws Exception {
datas = view.getEditModeType().getDataPackage(params, tempPage, LINES, user, currdoc);
//其他业务逻辑
}
׃q案例可看到Q引入subclass代替type code可以大大减少if-else判断Q而且可以把责d聚到每种type中,使代码结构更清晰易懂?/span>
在本案例中引入了I类型概念,?/span>NullEditModeQ代码如下:
/**
*
* @author nicholas zhen
*
*/
public class NullEditMode extends AbstractEditMode implements EditMode {
public NullEditMode(View view) {
super(view);
}
public String getQueryString(ParamsTable params, WebUser user, Document sDoc) {
return "";
}
public DataPackage getDataPackage(ParamsTable params, WebUser user, Document doc) throws Exception {
return new DataPackage();
}
public DataPackage getDataPackage(ParamsTable params, int page, int lines, WebUser user, Document doc) throws Exception {
return new DataPackage();
}
public long count(ParamsTable params, WebUser user, Document doc) throws Exception {
return 0;
}
}
说明Q空cd保证了调用每个方法都有默认D回,而不需要进行非I判断,当View没有cdӞ卌回默认的I类?/span>
原创人员QNicholas
转蝲人员QNicholas