Contract contract = new Contract();
contract.begin();
assertEquals(Running.class, contract.status.class)
private Rectangle empty;
public void setUp(){
empty = new Rectangle(0,0,0,0) // Fixture
}
public void testEmpty(){
assertTrue(empty.isEmpty());
}
public void tesetRate(){
exchange.addRate("USD", "GBP", 2);
int rate = exchange.findRate("USD", "GBP");
assertEquals(2, rate);
}
public void testMissingRate(){
try{
exchange.findRate("USD", "GBP");
fail();
} catch (IllegalArgumentException e){
...
}
}
TDD에서 디자인 패턴의 쓰임세
패턴 | 테스트 작성 | 리팩토링 |
---|---|---|
커맨드 | X | |
값 객체 | X | |
널 객체 | X | |
템플릿 메서드 | X | |
플러거블 객체 | X | |
플러거블 셀렉터 | X | |
팩토리 메서드 | X | X |
임포스터 | X | X |
컴포지트 | X | X |
수집 매개 변수 | X | X |
Runnable
interface Runnable
public abstract void run();
널리 공유해야 하지만 동일성(identity)은 중요하지 않을 때 객체를 어떤 식으로 설계할 수 있을 까? -> 객체가 생성될 때 객체의 상태를 설정한 후 이 상태가 절대 변할 수 없도록 한다. 그리고 이 객체에 대해 수행되는 연산은 언제나 새로운 객체를 반환하게 만든다.
두 객체가 다른 객체에 대한 레퍼런스를 공유하고 있는데, 한 객체가 공유되는 객체의 상태를 변화시키면 다른 객체는 공유된 객체에서 나온 값들이 쓸모 없게 된다. 차라리 공유 객체의 상태에 의존하지 않게 설계하는 편이 나을 것이다. 이는 고전적인 별칭 문제다.
별칭 문제 해결 방법으로 한 가지는 현재 의존하는 객체에 대한 레퍼런스를 결코 외부로 알리지 않는 방법이다. 그 대신 객체에 대한 복사본을 제공하는 것이다. 또 다른 방법은 옵저버 패턴을 사용하는 것이다. 의존하는 객체에 자기를 등록해 놓고, 객체의 상태가 변하면 통지를 받는 방법이다.
모든 값 객체는 동등성을 구현해야 한다. 참고로 동일성(identity)와 동등성(equality)은 서로 다르다. 예를 들어 5백원 동전 두 개가 서로 동등할지라도 동일하지는 않다.
즉, 값 객체는 해당 객체의 주소 값이 달라도 값(Structural Equality,구조적 동등성)이 같으면 동일하다고 생각 할 수 있다.
public boolean setReadOnly() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkWrite(path);
}
return fs.setReadOnly(this);
}
// null 객체
- LaxSecurity
public void canWrite(String path){
}
- SecurityManager
public static SecurityManager getSecurityManager(){
return security == null ? new LaxSecurity() : security;
}
- File
public boolean setReadOnly() {
SecurityManager security = System.getSecurityManager();
security.canWrite(path);
return fileSystem.setReadOnly(this);
}
class TestCase{
public void runBare() throws Throwable {
setUp();
try{
runTest();
}
finally {
tearDown();
}
}
}
if circle: then {
...
} else{
...
}
class SelectionTool{
Figure selected;
public void mouseDown(){
selected = findFigure();
if(selected != null){
select(selected);
}
}
public void mouseMove(){
if(selected != null){
move(selected);
} else{
moveSelectionRectangle();
}
}
public void mouseUp(){
if(selected == null){
selectAll();
}
}
}
class SelectionTool{
SelectionMode mode;
public void mouseDown(){
selected = findFigure();
if(selected != null){
mode = new SingleSelection(selected);
} else{
mode = new MultipleSelection();
}
}
public void mouseMove(){
mode.mouseMove();
}
public void mouseUp(){
mode.mouseUp();
}
}
인스턴스별로 서로 다른 메서드가 동적으로 호출되게 하려면 어떻게 해야할까? -> 메서드의 이름을 저장하고 있다가 그 이름에 해당하는 메서드를 동적으로 호출하도록 한다.
abstract class Report{
abstract void print();
}
class HTMLReport extends Report{
void print(){..}
}
class XMLReport extends Report{
void print(){..}
}
abstract class Report{
String printMessage;
Report(String printMessage){
this.printMEssage = printMessage;
}
void print(){
switch(printMessage){
case "printHTML":
printHTML();
break;
case "printXML":
printHTML();
break;
}
}
void printHTML(){
}
void printXML(){
}
}
void print(){
Method runMethod = getClass().getMethod(printMessage, null);
runMethod.invoke(this, new Class[0]);
}
class Money{
static Dollar dollar(int amount){
return new Dollar(amount);
}
}
testRectangle(){
Drawing d = new Drawing();
d.addFigure(new RectangleFigure(0, 10, 50, 100));
RecordingMedium brush = new RecordingMedium();
d.display(brush);
assertEquals(' rectangle 0 10 50 100 \n', brush.log());
}
testOval(){
Drawing d = new Drawing();
d.addFigure(new OvalFigure(0, 10, 50, 100));
RecordingMedium brush = new RecordingMedium();
d.display(brush);
assertEquals(' Oval 0 10 50 100 \n', brush.log());
}
public Transaction{
Transaction(Money value){
this.value = value;
}
}
public Account{
Transaction transactions[];
Money balance(){
Money sum = Money.zero();
for (int i =0; i < transactions.length; i++){
sum = sum.plus(transactions[i].value);
}
return sum;
}
}
interface Holding{
Money balance();
}
// Leaf
class Transaction implements Holding{
@Override
Money balance(){
return value
}
}
// Composite
class Account implements Holding{
Holding holdings[];
@Override
Money balance(){
Money sum = Money.zero();
for (int i =0; i < holdings.length; i++){
sum = sum.plus(holdings[i].balance);
}
return sum;
}
}
ex) java.io.Externalizatble 인터페이스 예제
public interface Externalizable extends java.io.Serializable{
void writeExternal(ObjectOutput out) throws IOException;
}