4 ms·
Finally, somebody talking sense. You define a class to encapsulate rules (data, permissions) specific to a class of problems in your domain so that you don't h
by lusr 14y ago
Finally, somebody talking sense. You define a class to encapsulate rules (data, permissions) specific to a class of problems in your domain so that you don't have to repeat those rules elsewhere, which has all sorts of problems (the worst of which become impossible to address in some scenarios, e.g. where one executes potentially malicious code).
If you're passing in DateTime.Now to your Publish method then the caller (probably a UI element or a web callback) is implementing business logic that does not belong to it, which violates the single responsibility principle and will eventually cause your code to become unmaintainable.
Part of the problem with these posts is the authors seldom have experience building software in some of the more tricky real-world software development environments. While DHH is rightfully respected for many things, I can find nothing in his history [1] to suggest he has the relevant experience to criticise "enterprise development": as far as I can see, he's never had to work on a millions-of-lines code base with 3+ years of cruft written and maintained by large disparate teams of mediocre developers using poorly chosen technologies to solve problems within time, quality and scope constraints outside his control.
In these sorts of environments, saying "start over" or "choose better" is not an option and things like DI and well designed interfaces make it possible for a small team of architects to ensure the mediocre developers actually accomplish something and software gets released.
I'll repost what I posted yesterday on the other post:
interface IArticleService {
void Publish(int id);
}
class ArticleService : IArticleService {
protected IArticleRepository ArticleRepository;
protected ITimeService TimeService;
public ArticleService(IArticleRepository articleRepository, ITimeService timeService) {
ArticleRepository = articleRepository;
TimeService = timeService;
}
public void Publish(int id) {
ArticleRepository.Publish(id, TimeService.Now);
}
}
Some features of this design pattern:
- the consumer of the service doesn't need to know anything about the publish time: e.g. whether it's UTC or local time, whether it needs to have +1 second added to deal with a bug in some legacy interface, or perhaps a switch to database time, etc. The problem of "publish time" is strictly limited to the ArticleService, which is an expert on the matter of publish times.
- any new business rules regarding publish times or Publish-related activities are strictly limited to this service and none of the consumers have to be modified when the rules change
- the ArticleService is easily testable by swapping in one or more testing ITimeService implementations (e.g. one that returns a fixed test date time, one that returns bad times, etc.)
- your software has a consistent, single view of time (which is critical in a time dependent application, for example)
[1] http://en.wikipedia.org/wiki/David_Heinemeier_Hansson http://en.wikipedia.org/wiki/David_Heinemeier_Hansson
- jurre 14y ago>as far as I can see, he's never had to work on a millions-of-lines code base with 3+ years of cruft written and maintained by large disparate teams of mediocre developers using poorly chosen technologies to solve problems within time, quality and scope constraints outside his control. But he doesn't say that it's bad to use DI in those situations, does he? Just that it's probably a bad idea to do it when you're working on a ruby project.
- lusr 14y agoIf you're building a simple system then it probably won't matter what you do. My opinion is it's good practice to follow sound architectural principles and just because your language lets you get away with crazy stuff doesn't necessarily mean it's the best choice. I listed a number of features of the approach I took - I'm not sure why I'd want to give all that up just to save a few lines of code.
- btilly 14y agoIf you're building a simple system then it probably won't matter what you do. Nothing could be farther from the truth. When you're building simple systems you're up against hard limits for efficient team size - you need the system to remain simple. The reason is that a totally flat team structure only remains incredibly productive until you get to 5-8 people or so. Then you're stuck. Adding a person adds more communication overhead than useful work. Adding process makes everyone less efficient. The result is that you need to do both - and according to published literature don't get your old level of productivity back until your team has 20+ people on it. Therefore the prime rule is to maximize efficiency. If you're going with the "small team of competent people" approach you can assume competence, but need to do what is efficient. Efficiency here means long-run efficiency. This isn't just "throw together spaghetti and hope it works". This is throwing together stuff fast, and making it maintainable by a small team (partly through keeping it simple enough that people can hold program state in their heads). I listed a number of features of the approach I took - I'm not sure why I'd want to give all that up just to save a few lines of code. As long as the code is not crazy, lines of code is approximately the same as effort. Therefore adding lines of code reduces efficiency. You also added complexity - which gives more to think about during debugging which also reduces efficiency. It is true that you gain a number of advantages. However the advantages you name fall under the YAGNIY principle. You Ain't Gonna Need It Yet. If you do need it, rewriting stuff to add that is likely to be a reasonable amount of work. In the meantime we save effort in writing, save effort in comprehending, and get to move on faster if we just leave it out.