Seam定义:
A seam is a place where you can alter behavior in your program without editing in that place。
bool CAsyncSslRec::Init() { if (m_bSslInitialized) { return true; } m_smutex.Unlock(); m_nSslRefCount++; m_bSslInitialized = true; FreeLibrary(m_hSslDll1); m_hSslDll1=0; FreeLibrary(m_hSslDll2); m_hSslDll2=0; if (!m_bFailureSent) { m_bFailureSent=TRUE; PostReceiveError(SOCKETCALLBACK, SSL_FAILURE); } CreateLibrary(m_hSslDll1,"syncesel1.dll"); CreateLibrary(m_hSslDll2,"syncesel2.dll"); m_hSslDll1->Init(); m_hSslDll2->Init(); return true; }
例如上面这段C++,如果PostReceiveError是一个严重依赖其他子系统的全局函数,并且这个子系统在测试的时候难以交互,用什么方式可以绕过PostReceiveError函数呢?
可以这样:
void CAsyncSslRec::PostReceiveError(UINT type, UINT errorcode) { ::PostReceiveError(type, errorcode); }
在CAsyncSslRec中写一个同名的方法,然后它的实现委托给全局的PostReceiveError方法。
其中::在C++中是域操作符(scoping operator),在Java中是没有的
class TestingAsyncSslRec : public CAsyncSslRec { virtual void PostReceiveError(UINT type, UINT errorcode) { } }
在测试类中写一个同样的方法,可以在测试的时候调用测试类中的PostReceiveError。
以上方法叫做Object seam. 原书定义如下(不翻译了):
We were able to change the method that is called without changing the method that calls it.
Seam的类型:
Preprocessing Seams,主要用于C/C++(跳过没看)
Link Seams
Object Seams:在OOP中用处比较大
Link Seams的Java例子。
有如下类:
package fitnesse; import fit.Parse; import fit.Fixture; import java.io.*; import java.util.Date; import java.io.*; import java.util.*; public class FitFilter { public String input; public Parse tables; public Fixture fixture = new Fixture(); public PrintWriter output; public static void main (String argv[]) { new FitFilter().run(argv); } public void run (String argv[]) { args(argv); process(); exit(); } public void process() { try { tables = new Parse(input); fixture.doTables(tables); } catch (Exception e) { exception(e); } tables.print(output); } ... }
在这个类中,引用了fit.Parse 和 fit.Fixture,JVM如何找到这些类呢?通过classpath环境变量。
可以创建同样名字的类,把它们放到别的目录中,修改classpath(enabling point),link到我们加的fit.Parse 和 fit.Fixture,虽然再生产环境中使用有些费解,但是这个方法在测试的时候是一个好的切入点。
在OOP中,继承关系中很多方法调用都是seam,但并不是所有的都是seam,比如这种:
public void method1(){ List<Integer> list = new ArrayList<Integer>(); list.add(1); }
因此此处没有enabling point,我们如果想换一个add方法,必须要修改代码。
但是下面这种是seam,enabling point是method1的参数(这个好理解,毕竟多态的作用就是消除类型之间的耦合关系):
public void method1(List<Integer> list){ list.add(1); }
书中举了个Cell类的例子,异曲同工
public class CustomSpreadsheet extends Spreadsheet { public Spreadsheet buildMartSheet(Cell cell) { ... cell.Recalculate(); ... } }