What if we use dependency injection?
1. Wrap the `lock` keyword in a custom class.
2. Pass in that custom object
3. In tests, pass a mock of that custom object instead.
Here's the resulting implementation of Counter:
class Counter{readonly ILock @lock;public Counter(ILock @lock){this.@lock = @lock;}internal int MoveNext(){using (this.@lock.Acquire()){return CurrentValue++;}}public int CurrentValue { get; private set; }}
One way of looking at what I've done here is addressed the Primitive Obsession around the _syncRoot object in the conventional pattern. You can't get much more primitive than System.Object!
You can see the resulting code here: https://github.com/JayBazuzi/TddLocks/tree/IDisposableLockToken/LocksExperiment1/LocksExperiment1
One thing to look at is my commit history: https://github.com/JayBazuzi/TddLocks/commits/IDisposableLockToken/LocksExperiment1/LocksExperiment1
Note that I explicitly called out which steps were RED, GREEN, and REFACTOR.
5b36285 REFACTOR; Rename `MyLock` -> `Lock`
c149f15 REFACTOR: Rename `Lock()` -> `Acquire()`
66518d7 GREEN: Simple `Lock()` and `IsLocked`
9b27a8c RED: LockedLockShouldBeLocked
Also, as the code currently stands (https://github.com/JayBazuzi/TddLocks/tree/IDisposableLockToken/LocksExperiment1/LocksExperiment1), there are 3 implementations of ILock:
MonitorLock, which attempts to duplicate the behavior of the `lock` keyword in C#, according to the language specification.
MockLock, which makes it easy to confirm that code is locking as expected.
SingleThreadedLock, which actually doesn't lock at all.
I created the different Lock implementations with TDD. All 3 pass the exact same set of unit tests.
Fun stuff.
No comments:
Post a Comment