A coworker just asked me this question - what's the point of delegates in .NET? My answer was very short and one that he had not found online: to delay execution of a method. Consider this: if you pass a Method2 as a parameter to Method1, Method2 is evaluated immediately and the result is passed into Method1. public class SomeObject { public void Method1(int someValue) { //... code here } } ... someObject.Method1(anotherObject.Method2());
Additionally, Method2 must have a return value (not void) so that the value returned from Method2 can be passed as the parameter of Method1.
With a delegate, though, we get method pointers that can be used to delay the execution of the method in question until it's needed (if at all). We can also get rid of the requirement for a return value and we can pass values that were created by the host method, into the delegate method.
public class SomeObject { public void Method1(Action<int> someAction) { //.. do some stuff here int aValue = GetTheValueFromSomeWhere(); someAction(aValue); //... do some more here } } public class AnotherObject { public void Method2(int aValue) { //do something that is dependent on aValue, here. int i = aValue + 1; // or anything else that needs aValue. } }
FYI - Action (void return; with many parameterized variations) is a delegate that is build into .NET 3.5, along with Func<T> (return value of type T; with many parameterized variations). These are the two most common delegates that I use in my code.
Most .NET developers have used delegates without realizing it, too. The event system in .NET is nothing more than a multi-cast delegate - a delegate that points to more than one method - with a special signature and syntax (depending on the language being used).
There's a lot of good use for delegates. And like any other tools, there's a lot of bad uses. Take the time to learn what they are, how they work, and where they can be beneficial.
FYI - I'll be giving my S.O.L.I.D. Software Principles presentation at the Austin .NET User Group on Monday the 13th. This is the same presentation that I gave at Pablo's Day(s) of TDD last weekend, except I'll have the missing code in place and the slide errors fixed. Here's the abstract that was posted via the ADNUG mailing list: S.O.L.I.D. Software Development: Achieving Object Oriented Principles, One Step At A Time Almost every professional software developer understands the academic definitions of Coupling, Cohesion, and Encapsulation. However, many of us do not understand how to actually achieve Low Coupling, High Cohesion, and strong Encapsulation, as prescribed. Fortunately, there are a set of stepping stones that we can use to reach these end goals, giving us a clear cut path to software that is easier to read, easier to understand, and easier to change. This presentation will define not only the three object oriented goals, but also the five S.O.L.I.D. principle that lead us there, while walking through a sample application. At some point this week or next, I will have my slides and code posted for people to download. (I know I promised this at PDoTDD. I got busy. Sorry). And as usual, there will likely be a group of people heading to Rudy's after the meeting, for extended discussions and snappy banter.
A few months ago, I posted some thoughts and questions on the proper use of Inversion of Control (IoC) containers and the Dependency Inversion (DI) principle. Since then, I've had the opportunity to do some additional study and teaching of DI, and I've had that light bulb moment for the proper use of an IoC container. I haven't talked about it or tried to present the info to my team(s) yet, because I had not verified my thoughts were on the right track - until recently. I got to spend a few hours at the Dovetail office with Chad, Jeremy, Josh, etc, and had the pleasure of being able to pick their brains on some of the questions and thoughts that I've had around DI and IoC. In the end, Chad confirmed some of my current thoughts and helped me put into a metaphor that I find to be very useful in understanding what Dependency Inversion really is - a cloud objects that can be strung together into the necessary hierarchy, at runtime. Consider a set of classes that need to be instantiated into the correct hierarchy so that we can get the functionality needed. It's really easy to have the highest level class - the one that we really want to call method on - instantiate the class for next level down, and have that class instantiate it's next level down, and so-on, like this: This creates the necessary hierarchy, but breaks the core object oriented principle of loose coupling. We would not be able to use ThisClass without bringing ThatClass along with it, and we would not be able to use ThatClass without bringing AnotherClass along with it. By introducing a better abstraction for each class and putting Dependency Inversion into play, we can break the spaghetti mess apart and introduce the ability to use any of these individual classes without requiring the specific implementation of the dependent class. For starters, let's introduce an interface for ThisClass to depend on and an interface for ThatClass to depend on. Now that we have an interface that both of these classes can depend on, instead of the explicit implementation of the child object, we need to have the expected child object implement the interface in question. For example, we expect ThatClass to be used by ThisClass, so we will want ThatClass to implement the IDoSomething interface. By the same notion, we want AnotherClass to implement the IWhatever interface. This will allow us to provide AnotherClass as the dependency implementation for ThatClass. Our object model now looks like this: What we have now is not just a set of classes that all depend on each other, but a "cloud" of objects with dependencies and interface implementations that will let us build the hierarchy we need, when we need it. The real beauty of this is that we no longer have to care about the implementation specifics of IDoSomething from ThisClass. ThisClass can focus entirely on doing it's duty and calling into IDoSomething when it needs to. And, by passing in the dependency as an abstraction, we're able to replace the dependency implementation at any time - runtime, unit test time, etc. This also makes our system much easier to learn and understand, and most importantly - easier to change. Now that we have our cloud of implementations and abstractions in place, we will need to reconstruct the hierarchy that we want so we can call into ThisClass and have it perform it's operations. Here's where Dependency Inversion meets up with Inversion of Control. - To create ThisClass, we need an implementation of IDoSomething
- ThatClass implements IDoSomething, so we'll instantiate it before ThisClass
- ThatClass needs an instance of IWhatever
- AnotherClass implements IWhatever, so we'll instantiate it before ThatClass
- Once we have AnotherClass instantiated, we can pass it into ThatClass's constructor
- Once we have ThatClass instantiated, we can pass it into ThisClass's constructor
We end up with a hierarchy of objects that is instantiated in reverse order, like this: We have now successfully inverted our system's construction - each implementation detail is created and passed into the the object that depends on it, re-creating our hierarchy from the bottom up. In the end, we have an instance of ThisClass that we can call into, with the same basic hierarchy of classes that we started with. The real difference is that now we can change this hierarchy at any time without worrying about breaking the functionality of the system. Once we have our Dependency Inversion and Inversion of Control in place, we can start utilizing the existing IoC frameworks to automatically create our hierarchy of objects based on the advertised dependencies (an advertised dependency is a dependency that is specified as a constructor parameter of a class). Tools like StructureMap, Spring.net, Windsor, Ninject, and others, all provide automagic ways of creating each dependency of the object that is requested, all the way up/down the hierarchy. Utilizing one of these IoC containers can greatly simplify our code base and eliminate the many object instantiations that would start to liter our code. As I said in my previous post, I know all about what not to do with IoC containers. Good IoC usage, though, is another subject for another post.
It's official - I'm now a member of Los Techies. I will continue to post here, at DerickBailey.com, and will be cross-posting over at Los Techies once I figure out how to do that.  Thanks to Joe, Chad, and everyone else in the Los Techies crew, for the vote of confidence in adding me!
At some point, you will need to put a timer into your C# code. Hopefully you're doing Test Driven Development, or at least unit testing, to cover your need for a timer with a unit test. If you are - I'm betting you ran into some really ugly problems like tests that would not pass without having 'Thread.Sleep(1000)' calls in them. Worse yet - last week, I put a 3 second timer into a class that was being unit tested. This one mistake cause my CruiseControl build to go from 2 minutes, to timing out at 90 minutes because NUnit was keeping the app domain alive while the timer was alive, so it never closed the test run. Oops. To fix this problem, we decided we needed to eliminate the use of an actual timer and use an abstraction that could be mocked/controlled in our unit tests. Unfortunately, the .NET Framework does not have an actual abstraction for a timer. So, if you need to use a timer in your code - and at some point in time, you will - you'll want to create a very simple ITimer interface that would let you control when the timer elapses and fires its registered action. Here's the core of the abstraction that we came up with, initially. public interface ITimer { void Start(Action timerAction); }
With such a simple interface in place, we can now mock it out in our unit tests (I like Rhino Mocks) and set up an expectation on the Start method to capture the timerAction parameter, then simulate the timer elapsing by calling the timer action from our unit test. Here's an example test and 'system under test' to illustrate how we use this interface.
[Test] public void DemonstratingHowToUnitTestATimerElapsing() { Action timerElaspedAction; ITimer timer = MockRepository.GenerateMock<ITimer>(); timer.Expect(t => t.Start(null)).IgnoreArguments().Callback( delegate(Action timerAction) { timerElapsedAction = timerAction; return true; }); MySystemUnderTest sut = new MySystemUnderTest(); sut.StartMonitoringStuff(timer); //here's where we simulate the timer elapsing timerElapsedAction(); Assert.IsTrue(sut.TheTimerActionWasCalled); } public class MySystemUnderTest() { public bool TheTimerActionWasCalled { get; set; } public MySystemUnderTest() { TheTimerActionWasCalled = false; } public StartMonitoringStuff(ITimer timer) { timer.Start(() => TheTimerActionWasCalled = true); } }
There is a bit more detail to the actual ITimer interface, and the implementation, though. We ran into issues where we needed to stop the timer, prevent it from firing after we close the SUT, etc. So, all said and done, here's the full ITimer interface and MyTimer implementation that we ended up with.
public interface ITimer { void Start(Action action); void Stop(); } public class MyTimer : ITimer, IDisposable { private TimeSpan _timerInterval; private Timer _timer; private Action _timerAction; private bool IsRunning { get; set; } public MyTimer(TimeSpan timerInterval) { _timerInterval = timerInterval; } public void Dispose() { StopTimer(); } public void Start(Action action) { _timerAction = action; IsRunning = true; StartTimer(); } public void Stop() { IsRunning = false; StopTimer(); } private void StartTimer() { _timer = new Timer(o => Timer_Execute(), null, 0, Convert.ToInt32(_timerInterval.TotalMilliseconds)); } private void StopTimer() { if (_timer != null) { _timer.Change(Timeout.Infinite, Timeout.Infinite); _timer.Dispose(); _timer = null; } } private void Timer_Execute() { try { StopTimer(); _timerAction(); } finally { if (IsRunning) StartTimer(); } } }
This has worked out very well for us, so far. It lets us put a timer in place when we need one, but not worry about thread racing issues, long running tests, etc.
I've seen this question a lot recently, and Scott Bellware asks it again in response to a post by Jimmy Bogard. "Ok, but why are "contracts" important? I can write concrete classes that interact with each other just fine without interfaces. And as Roy has already demonstrated, you don't need interfaces for test-focused SoC. To wit, any concrete class's signature is a contract with its user." -- Scott Bellware First - a small psychology lesson in Cognitive Load theory and Chunking. A human brain can only hold so much information in it's short term memory. The "magic number" of 7 (+/- 2) has often been touted as the average number of concepts that a person can hold in short term memory, and still understand what's going on. ... and we need to recognize this in our code. If a developer who is reading the code has more than 7 (+/- 2) concepts, then that developer is not likely to understand the code they are reading. If you can't understand the code, you can't maintain the code. It's as simple as that. So why are contracts important? An intention-revealing interface, as a contract, can significantly reduce the cognitive load that is required to understand the code in question. Abstraction and dependency inversion via interfaces help us achieve this understandability by letting a developer's mind chunk a process into what's really important - the "when" and "what" of the process, ignoring the "how". If you eliminated all abstractions and interfaces - even the interfaces that have only one implementation - you are telling a developer that they need to know the details of "how", not just the abstraction of "when" and "what". This puts an additional load on any persons' brain, and can quickly overload the person reading the code. These theories go so far beyond just code, in software. When was the last time you saw a web site that had more than 8 or 9 buttons in it's navigation/menu, and you thought it was an intuitive and easy to use site? I'd be willing to bet that you thought the site was poorly organized and difficult to navigate. Interaction design is usually the first place that people apply cognitive load and chunking theories. Unfortunately, it's also usually the last. We need to break this cycle of overloading ourselves and our coworkers, and create proper abstractions in our code that fit easily within our own cognitive load, but more importantly in the cognitive load of other developers who have to read/maintain the code.
After some additional Twitter discussion with Jimmy last night, I realized that my previous response had neglected to distinguish between two very important contexts - new code and legacy code. Considering a legacy system (as defined by Michael Feathers), I'm 100% on board with everything Jimmy is talking about and all of the techniques, troubles, and diminishing returns that he identifies. I still maintain that 100% coverage should be the default result of true TDD when writing new code - and I apply this philosophy when writing new code inside of a legacy system. Write a test to prove you need the new code that you are writing... unfortunately, the distinction between new and legacy code does get blurry and can be difficult to navigate when working in a legacy system. For more info on legacy code, in this context, and how to deal with it:
I've talked about code coverage and software quality with a number of people on Twitter in the last month or so, including Jimmy Bogard, who has a nicely written post about Quality and code coverage. It's no secret that I have some fairly radical opinions on code coverage and that I don't agree with Jimmy in these regards. I firmly believe that 100% code coverage, in whatever form we can get, is a reasonable goal and not past the point of diminishing returns. As such, I'd like to respond to some of what Jimmy is saying and expand on it with my own thoughts. But first - I don't like to draw hard lines between "Unit" tests and "Integration" tests - that just blurs the problem even more because we can argue that you are covering code or not, in "unit" tests all day long, based on this blurry line. This is not the discussion I want to have, so I am lumping "Unit" and "Integration" tests into "Unit" tests as a whole. Secondly, I'd like to say that code coverage is not always an indication of quality - in poorly written systems, it's merely a measure of code coverage. However, quality code is another subject of much debate. On to the response! ............................................. "The idea is that the team, all practicing TDD, should dutifully measure and add unit tests until they reach the assumed pinnacle of unit testing: 100% coverage." There are several assumptions and statements in this that I don't believe are true. For one, TDD and Unit Testing are not the same - no where near the same. Unit Testing lets you add tests as you see fit - after you write the code, while you write the code, whenever you want. You can even do test-first unit testing. True TDD, however, is a pure pragmatic approach to software development in that you never write code that does not have proof of need to exist - a test that says it needs to be there. The "pinnacle" of TDD and Unit Testing are not the same thing. Unit Testing - the act of adding unit tests when you see the need - may have a goal of 100% coverage. TDD, on the other hand, has a goal of only implementing the code that has been specified as needed. 100% coverage is the default in TDD because you never write code you don't need. If you are going through the "measure and add unit tests" process, I would put money down to say that you are doing Unit Testing and very little TDD. The process should be something more like "measure to make sure we haven't added anything we don't need yet". 100% coverage is a side effect of TDD. "The general motivation behind 100% coverage is that 100% coverage equals zero bugs." Unfortunately, this is the motivation for many people who write unit tests - but it's the wrong motivation. You will never be 100% bug free just because you have 100% code coverage. There will always be some business case or external system factors that cause a bug now and then. That doesn't mean we should be ok with letting bugs into our system. On the contrary, one of our motivations (not the only one) should be to prevent as many bugs as possible from getting into the system. "NCover is a powerful tool, but it still doesn't support all types of coverage. Attaining 100% coverage in NCover still means there are paths that we haven't tested yet, which means there are still potential bugs in our code. " I think the logical conclusion of this argument is that if we don't have a tool that supports true 100% coverage, then we shouldn't use that tool at all. I know I'm inserting my own conclusions into Jimmy's words - that's simply the conclusion that I came to from these statements. And this is a very bad conclusion. Do you walk around naked simply because wearing clothes would get them dirty and you'd have to wash them? Both of these are akin to the broken window syndrome. Use the tools that are available to the extent that they can operate, and find better tools when you can. "If 100% coverage is a goal," 100% coverage should NEVER be a goal - it's merely an indication that you are on the right path and working pragmatically. "In recent projects where we measured coverage several months in to the project, we saw regular numbers of 90% coverage. This was on a team doing 100% TDD." ... "So are we doing TDD wrong?" So, are you doing TDD wrong? Yes. You are unit testing in the guise of Test-First-Development and not adhering to the spirit or pragmatism that TDD wants. "Every test introduced covered behavior we considered interesting. If behavior isn't interesting, we don't care about it. " If you have code that is not interesting and you don't care about it - why is it in the system? If you don't care about the code, then it makes no difference whether or not that code works correctly. I would be appalled to hear that this is true. If it is true, then that code should not be in the system to begin with. "If tests are a description of the behavior of the system, why fill it with all the boring, trivial parts? The effort required to cover triviality is just too high compared to other ways we can increase value." I think your definition of "behavior" is wrong. From wordnet.princeton.edu/perl/webwn: Behavior: "the action or reaction of something (as a machine or substance) under specified circumstances;" So, how is throwing an exception when a parameter is null, not behavior? If you are doing defensive programming to make sure you have all the data you need, then you are creating behavior - system behavior, not business process behavior. Behavior is still behavior, though. The only possibility of code not being behavior is a simple data point. If you have a data point in your code that is not covered by a unit test of behavior, then you don't need that data point. Yes, yes, I know - "integration", "nhibernate", "not my data source", "it has to be there for ...". If you argue that you need a property for some external portion of the system to operate correctly, then you should be proving that you need the property by covering it with a unit test that specifically says what it is for and shows how it is used. If you don't, then there's no way of knowing why the property exists and it may be deleted because it looks like nothing in the system uses it. Or, possibly worse, you end up with a lot of dead code in your system because you are afraid of deleting anything. "Missing in the 100% coverage conversation is the effort required to get to 100%. Attempting to get another 5% takes equal effort of the previous 10%. The next 2% takes equal effort of the previous 15%, and so on." I'd like to know where you got the math. If you are talking about unit testing - even test-first unit testing - i can understand the pain you're talking about, here. Why should I bother setting up another test fixture and getting all the state of my objects in place for a simple null reference check? blech - that totally sucks, is boring, is tedious, etc. However, my typical process of achieving 100% is to only write code that is needed for the system to work, by specifying how the system will work through tests and then coding it. If I have a null reference check somewhere in my code it should be because I already have a test that specifies why it's needed. "This is called the law of diminishing returns. As we get closer and closer to 100%, it takes vastly more effort to get there. At some point, you have to ask yourselves, is there value in this effort? " Here's the real crux of the problem - you are assuming that you started with, or at some point has less than 100% coverage. It takes zero additional effort to stay at 100% coverage if you start at 100% coverage and maintain it through pragmatic TDD. "Often, bending code to get 100% can decrease design quality, as you're now twisting the original intent solely for coverage concerns, not usability, readability or other concerns." If you find yourself bending code to get 100% coverage, you have one of two situations (if not both): 1) poor design in your code, 2) poorly written tests. I would bet that you have both because they are a vicious circle. "Measuring coverage is an interesting data point, as are other measures such as static analysis. But in the end, it's only a measure, an indication. " 100% agreed! "It's still up to the team to decide on the value of addressing missing areas," This implies that you don't have 100% coverage, already. In this situation, 100% agreed. I'm in this situation right now - it's hard to know what tests create the most value, at this point. However, any new code - even code changes to existing code - are done TDD style. So, while we don't yet have 100% coverage, we are increasing coverage all the time by never writing code without a test (that's part of the goal, anyway... no one is perfect, and it takes serious discipline to do this) "with the full knowledge that they are still limited to what the tool measures." Agreed, again. If you don't know the limitations of the tools you are using, you're in trouble. ............................................. The boilerplate argument that I am making is that you should never write code that is not required by a customer / consumer. However, as I've Previously Talked About, there's a high likelihood that you have not identified all of your customers / consumers. Take NHibernate, for example. I have a project that needs about 30 different properties for a specific NHibernate query to be executed. The NHibernate query is the only place they are ever used - no code reads them, only a search screen writes to them, then the NHibernate query loads data from the table that they are mapped to. When I first wrote this code, my coverage was at around 30%. This situation is one of many that I've been in recently, that has lead me to believe that other code must be considered a customer / consumer of your code. If I deleted any of these properties, then the NHibernate query would fail. Therefore, the properties are valuable to NHiberate. Since these properties are valuable to at least one of the consumers of my code, I should prove that they need to exist by writing a test that proves they are needed and why.
I gave a presentation to my team on S.O.L.I.D. software development principles, talking about how these five principles enable us to achieve High Cohesion, Low Coupling, and proper Encapsulation in our software projects. If you're unfamiliar with the SOLID principles, they are: - SRP: Single Responsibility - One reason to exist, one reason to change
- OCP: Open Closed Principle - Open for extension, closed for modification
- LSP: Liskov Substitution Principle - An object should be semantically replaceable for it's base class/interface
- ISP: Interface Segregation Principle - Don't force a client to depend on an interface it doesn't need to know about
- DIP: Dependency Inversion Principle - Depend on abstractions, not concrete detail or implementations
You can find a lot of good info on SOLID via Robert Martin (the man who coined the acronym) and the Los Techies crew: The final slide in my presentation shows the before and after of migrating a small app that reads a file and sends an email, from being 100% coded in the Winforms code, to a SOLID code structure. During the summary / review, I related the five SOLID principles to High Cohesion, Low Coupling, and Encapsulation through these descriptions: Low Coupling: By abstracting many of our implementation needs into various interfaces and introducing the concepts on OCP and DIP, we’ve created a system that has very low coupling. Many of these individual pieces can be taken out of this system with little to no spaghetti mess trailing after it. Separating the various concerns into the various object implementations has also helped us ensure that we can change the system’s behavior as needed, with very little modification to the overall system – just update the one piece that contains the behavior in question. High Cohesion: This really is a direct result of low coupling and SRP – we have a lot of small pieces that can be stacked together like building blocks to create something larger and more complex. Any of these individual pieces may not represent much functionality or behavior, but then, an individual piece isn’t much fun to use without a bunch of other pieces. DIP has also allowed us to tie the various blocks together by depending on an abstraction and allowing that abstraction to be fulfilled with different implementations, creating a system that is much greater than the mere sum of it’s parts. Encapsulation: True encapsulation is not just making fields private and hiding data from external objects – but hiding implementation details from other objects, depending only on the abstractions and expected behaviors of those abstractions. LSP, DI, and SRP all work hand in hand to create true encapsulation in the new project structure. We’ve encapsulated our behavioral implementations in many individual objects, preventing them from leaking into each other – and we’ve ensured that the dependency on those behaviors is encapsulated behind a known interface. We’ve hidden the implementation details and allowed for any implementation to be put in place for that interface definition through DIP. At the same time, we’ve done the necessary due-diligence to ensure that we are not violating any of the individual abstraction’s semantics or purpose (LSP), ensuring that we can properly replace the implementation as needed. The end result of our effort has created a system that appears to be complex on the surface – after all, there’s now 13 blocks in our diagram compared to the original 2. However, the apparent complexity of this system is diminished by the simplicity of each individual interface and object. Many small, independent, simple pieces have been wired together to create a larger overall system behavior that can be described as complex. Yet any of these implementations can be changed and/or replaced – very easily. At some point, I'm hoping to post the entire slide deck and presentation / code, but for now I thought the summary that I sent to the team was worth sharing with the rest of the world.
|