Archief - [PROG][C++][Java] Factory Method / polymorfisme

Het archief is een bevroren moment uit een vorige versie van dit forum, met andere regels en andere bazen. Deze posts weerspiegelen op geen enkele manier onze huidige ideeën, waarden of wereldbeelden en zijn op sommige plaatsen gecensureerd wegens ontoelaatbaar. Veel zijn in een andere tijdsgeest gemaakt, al dan niet ironisch - zoals in het ironische subforum Off-Topic - en zouden op dit moment niet meer gepost (mogen) worden. Toch bieden we dit archief nog graag aan als informatiedatabank en naslagwerk. Lees er hier meer over of start een gesprek met anderen.

forloRn_

Legacy Member
Hallo,

Hieronder vind je dezelfde code in Java en C++.

Een beetje informatie: SIPAgent HAS-A SIPApp en initialiseert die member app in zijn constructor door middel van createSIPApp(). LocalSIPAgent is een subklasse van SIPAgent en overridet createSIPApp(), waarin hij een MockSIPApp teruggeeft.

Slimme mensen herkennen hierin een Factory Method en nog slimmere mensen weten dat MockSIPApp een zogenaamd mock object is, dat unit testing van SIPAgent vergemakkelijkt omdat de low level code (hier een verbinding met een server) wordt gebypassed.

Vraag: Java gebruikt netjes polymorfisme (zie main() method) en creëert een mock object, terwijl C++ volhardt in de boosheid en een echt SIPApp-object creëert. In het geval van Java wordt de server dus gebypassed, maar in het geval van C++ niet. Waarom?

Java

SIPAgent.java:
Code:
public class SIPAgent {
	public SIPAgent() {
		app = createSIPApp();
	}

	public void setCurrentAccount() {
		app.setCurrentAccount();
	}
	
	protected SIPApp createSIPApp() {
		return new SIPApp();
	}
	
	private SIPApp app;
	
	public static void main(String args[]) {
		SIPAgent agent = new LocalSIPAgent();
		agent.setCurrentAccount();
	}
}

LocalSIPAgent.java
Code:
public class LocalSIPAgent extends SIPAgent {
	protected SIPApp createSIPApp() {
		return new MockSIPApp();
	}
}

SIPApp.java:
Code:
public class SIPApp {
	public void setCurrentAccount() {
		System.out.println("used server");
	}
}

MockSIPapp.java:
Code:
public class MockSIPApp extends SIPApp {
	public void setCurrentAccount() {
		System.out.println("didn't use server");
	}
}

C++

SIPAgent.cpp:
Code:
#ifndef SIPAGENT_CPP
#define SIPAGENT_CPP

#include "SIPApp.cpp"

class SIPAgent {
	public:
		SIPAgent() {
			app = createSIPApp();
		}

		~SIPAgent() {
			delete app;
		}

		void setCurrentAccount() {
			app->setCurrentAccount();
		}

	protected:
		virtual SIPApp *createSIPApp() {
			return new SIPApp; 
		}

	private:
		SIPApp *app;
};

#endif

LocalSIPAgent.cpp:
Code:
#ifndef LOCALSIPAGENT_CPP
#define LOCALSIPAGENT_CPP

#include "SIPAgent.cpp"
#include "MockSIPApp.cpp"

class LocalSIPAgent: public SIPAgent {
	public:
		LocalSIPAgent() {}

	protected:
		SIPApp *createSIPApp() {
			return new MockSIPApp; 
		}

	private:
		SIPApp *app;
};

#endif

SIPApp.cpp:
Code:
#ifndef SIPAPP_CPP
#define SIPAPP_CPP

#include <iostream>

class SIPApp {
	public:
		void setCurrentAccount() {
			std::cout << "used actual SIP server\n";
		}
};

#endif

MockSIPApp.cpp:
Code:
#ifndef MOCKSIPAPP_CPP
#define MOCKSIPAPP_CPP

#include <iostream>
#include "SIPApp.cpp"

class MockSIPApp: public SIPApp {
	public:
		void setCurrentAccount() {
			std::cout << "uh uh, no server used\n";
		}
};

#endif

main.cpp:
Code:
#include "LocalSIPAgent.cpp"
#include "SIPAgent.cpp"

int main() {
	SIPAgent *agent = new LocalSIPAgent;
	agent->setCurrentAccount();
	delete agent;
	
	return 0;
}

forloRn_

Legacy Member
Deze versie is iets leesbaarder:

Code:
#include <iostream>
using std::cout;

class SIPApp {
	public:
		void setCurrentAccount() { cout << "SIPApp sets account\n"; }
};

class FakeSIPApp: public SIPApp {
	public:
		void setCurrentAccount() { cout << "FakeSIPApp sets account\n"; }
};

class SIPAgent {
	public:
		SIPAgent() { app = createSIPApp(); }
		virtual ~SIPAgent() { delete app; }

		void setCurrentAccount() { app->setCurrentAccount(); }

	protected:
		virtual SIPApp *createSIPApp() = 0; //{ return new SIPApp; }

	private:
		SIPApp *app;
};

class FakeSIPAgent: public SIPAgent {
	protected:
		SIPApp *createSIPApp() { return new FakeSIPApp; }
};

int main() {
	FakeSIPAgent agent;
	agent.setCurrentAccount();
}

Om te testen heb ik createSIPApp() in SIPAgent eventjes abstract gemaakt, waarop de compiler de volgende respons geeft:

Code:
make -k all 
Building file: ../test.cpp
Invoking: GCC C++ Compiler
g++ -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF"test.d" -MT"test.d" -o"test.o" "../test.cpp"
../test.cpp: In constructor ‘SIPAgent::SIPAgent()’:
../test.cpp:16: warning: abstract virtual ‘virtual SIPApp* SIPAgent::createSIPApp()’ called from constructor
Finished building: ../test.cpp
 
Building target: FactoryMethod
Invoking: GCC C++ Linker
g++  -o"FactoryMethod"  ./test.o   
./test.o: In function `SIPAgent':../test.cpp:16: undefined reference to `SIPAgent::createSIPApp()'
collect2: ld returned 1 exit status
make: *** [FactoryMethod] Error 1
make: Target `all' not remade because of errors.
Build complete for project FactoryMethod

Ik zou verwachten dat polymorfisme ervoor zorgt dat FakeSIPAgent::createSIPApp() wordt aangeroepen als ik een instance maak van FakeSIPAgent (zie main()). What gives? :unsure:

forloRn_

Legacy Member
Dit is het Java-equivalent en dat werkt wel. :doh:

Code:
class SIPApp {
	public void setCurrentAccount() { System.out.println("SIPApp sets account"); }
}

class FakeSIPApp extends SIPApp {
	public void setCurrentAccount() { System.out.println("FakeSIPApp sets account"); }
}

abstract class SIPAgent {
	public SIPAgent() { app = createSIPApp(); }
	
	public void setCurrentAccount() { app.setCurrentAccount(); }

	protected abstract SIPApp createSIPApp();// { return new SIPApp(); }
	
	private SIPApp app;
}

class FakeSIPAgent extends SIPAgent {
	protected SIPApp createSIPApp() { return new FakeSIPApp(); }
}

public class FactoryMethodMain {
	public static void main(String[] args) {
		SIPAgent agent = new FakeSIPAgent();
		agent.setCurrentAccount();
	}
}
Het archief is een bevroren moment uit een vorige versie van dit forum, met andere regels en andere bazen. Deze posts weerspiegelen op geen enkele manier onze huidige ideeën, waarden of wereldbeelden en zijn op sommige plaatsen gecensureerd wegens ontoelaatbaar. Veel zijn in een andere tijdsgeest gemaakt, al dan niet ironisch - zoals in het ironische subforum Off-Topic - en zouden op dit moment niet meer gepost (mogen) worden. Toch bieden we dit archief nog graag aan als informatiedatabank en naslagwerk. Lees er hier meer over of start een gesprek met anderen.
Terug
Bovenaan