CCR tips and tricks - part 5
There is one thing in CCR I think should not be there in its current form and that is the SuccessFailurePort. The reason I don't like its current implementation is that it can easily become a generic container of results where really other types should be used. Before I give an example you need to know what setup I used for all tests in today's post.
1: [TestClass]
2: public class SuccessFailurePortUsage
3: {
4: private DispatcherQueue dispatcherQueue = new DispatcherQueue();
5: private PortUtility portUtility;
6:
7: [TestInitialize]
8: public void Setup()
9: {
10: portUtility = new PortUtility(dispatcherQueue);
11: }
Now an example on what it looks like when the SuccessFailurePort is misused.
1: private SuccessFailurePort WhenItShouldNotBeUsed(string numberOfThingsOk)
2: {
3: var resultPort = new SuccessFailurePort();
4: int thingsOk = 0;
5: if (int.TryParse(numberOfThingsOk, out thingsOk))
6: {
7: resultPort.Post(new SuccessResult(thingsOk));
8: }
9: else
10: {
11: resultPort.Post(
12: new Exception("Somehting is not OK: " + numberOfThingsOk));
13: }
14: return resultPort;
15: }
16:
17: [TestMethod]
18: public void When_it_should_not_be_used()
19: {
20: using (var testCausality = new CausalityForTests(dispatcherQueue))
21: {
22: portUtility.Choice(
23: WhenItShouldNotBeUsed("42"),
24: s => Assert.AreEqual(42, s.Status),
25: e => Assert.Fail("Unexpected failure: {0}", e));
26: portUtility.Choice(
27: WhenItShouldNotBeUsed("Not a number"),
28: s => Assert.Fail("Unexpected success"),
29: CcrServiceBase.EmptyHandler);
30: }
31: }
As you can see the status field of the SuccessResult is used as a transport for a result. The correct PortSet to used in this case is really PortSet<int, Exception>. There is also the opposite situation where a SuccessFailurePort is not used but it really should be used. Typically it is a PortSet<bool, Exception> port where false (or true) is never actually posted like in this example.
1: private PortSet<bool, Exception> WhenItShouldBeUsed(bool allOk)
2: {
3: var resultPort = new PortSet<bool, Exception>();
4: if (allOk)
5: {
6: resultPort.Post(true);
7: }
8: else
9: {
10: resultPort.Post(new Exception("Somehting is not OK"));
11: }
12: return resultPort;
13: }
14:
15: [TestMethod]
16: public void When_it_should_be_used()
17: {
18: using (var testCausality = new CausalityForTests(dispatcherQueue))
19: {
20: portUtility.Choice(
21: WhenItShouldBeUsed(true),
22: CcrServiceBase.EmptyHandler,
23: e => Assert.Fail("Unexpected failure: {0}", e));
24: portUtility.Choice(
25: WhenItShouldBeUsed(false),
26: s => Assert.Fail("Unexpected success"),
27: CcrServiceBase.EmptyHandler);
28: }
29: }
In my opinion the only time you should use a SuccessFailurePort is when you use the shared instance like this.
1: private SuccessFailurePort TheRightWay(bool allOk)
2: {
3: var resultPort = new SuccessFailurePort();
4: if (allOk)
5: {
6: resultPort.Post(SuccessResult.Instance);
7: }
8: else
9: {
10: resultPort.Post(new Exception("Somehting is not OK"));
11: }
12: return resultPort;
13: }
14:
15: [TestMethod]
16: public void The_right_way()
17: {
18: using (var testCausality = new CausalityForTests(dispatcherQueue))
19: {
20: portUtility.Choice(
21: TheRightWay(true),
22: CcrServiceBase.EmptyHandler,
23: e => Assert.Fail("Unexpected failure: {0}", e));
24: portUtility.Choice(
25: TheRightWay(false),
26: s => Assert.Fail("Unexpected success"),
27: CcrServiceBase.EmptyHandler);
28: }
29: }
This is why I think the SuccessFailurePort is implemented in the wrong way. Since only the shared instance is used the SuccessFailurePort could have been implemented using the EmptyValue type like this.
1: private PortSet<EmptyValue, Exception> TheRightWayAlternative(bool allOk)
2: {
3: var resultPort = new PortSet<EmptyValue, Exception>();
4: if (allOk)
5: {
6: resultPort.Post(EmptyValue.SharedInstance);
7: }
8: else
9: {
10: resultPort.Post(new Exception("Somehting is not OK"));
11: }
12: return resultPort;
13: }
14:
15: [TestMethod]
16: public void The_right_way_alternative()
17: {
18: using (var testCausality = new CausalityForTests(dispatcherQueue))
19: {
20: portUtility.Choice(
21: TheRightWayAlternative(true),
22: CcrServiceBase.EmptyHandler,
23: e => Assert.Fail("Unexpected failure: {0}", e));
24: portUtility.Choice(
25: TheRightWayAlternative(false),
26: s => Assert.Fail("Unexpected success"),
27: CcrServiceBase.EmptyHandler);
28: }
29: }
Notice the code on line 27 in the last example? That's another good practice in CCR code. Whenever you have a handler that is empty you should use CcrServiceBase.EmptyHandler.