In my previous post I explained how I implemented a sudoku solver using PHP and OOP.
More or less, this is it. You can dig the code to see the details. But in this post I would like to share with you some thoughts about the designing and implementation process.
For me, it is the more interesting part. To be able to play with designing and coding without any time pressure and limitations. So, I can give the most of myself.
Of course, they are just my own opinions. Perhaps you agree with them. Perhaps you don’t. Software building is an activity complex enough to give room for more than one opinion.
The iterative process
When I start a new application on my own, my first step is to come up with some general idea about the involved classes and use cases.
The designing stage
Even thouh I take my time planning my classes, when I start coding I find lots of hidden behaviours and I need to do a lot of redesigning. This takes time.
I wonder if I can discover these hidden behaviours at the designing stage so my coding would be more straightforward. I will try this approach my next experiment.
As soon as I get comfortable with this preliminary design, I start coding bottom up. And the iterative process starts:
- I code a first draft of one supporting class.
- When I finish this draft, I start coding its related phpunit to test its public behaviour.
- It is very usual that when I start coding the phpunit, I find some missing additional behaviours of the tested class. Or I need to change its original behaviour. So I start coding both the class and its phpunit at the same time.
- When I have finished the supporting classes, I start the upper level classes.
- It is possible I find this upper level classes need some additional behaviour from the supporting classes. So I start refactoring them (I usually don’t add new phpunit test cases, I just refactor the phpunit just enough to make them work when needed).
- Once I finish the coding of this upper level class, I start phpunit it.
- And the cycle of coding of classes and phpunit repeats until I reach the use case level class.
It is important to highlight I try not to mock anything when I code my phpunits. In my phpunit I always try to use the original supporting classes. This way, when I test a class, I am testing it and all its supporting classes.
I find very enriching this approach: discovering new behaviours when I am coding both the classes and their phpunits and when I use them in the upper layers.
As you can see, I spend most of my time coding and refining Domain classes. I just deal with the entry point with the outside world in a very late stage.
Testing, testing, testing
It is clear I do a lot of testing. It means lots of time. But I think it really worths it:
- Testing the class along with the class itself allows me to think about it and refine the desired behaviour of the tested class.
- I do lots of refactoring. Phpunits assure me that whatever refactoring I am doing, my code is still healthy.
With so much testing and coding bottom up, when I implemented the upper level classes (in this project, the command line interface) it almost worked in my first try.
More testing, testing, testing
I am perhaps a bit obsessed about code coverage of my applications. It is very easy to check it in Phpstorm (when you have properly configured it).
When I stabilize my code and my code coverage is not as good as I expect. I review which parts of my code is not covered. This inspection usually raises some use cases I have not tested. So, I complete my phpunits to cover this missed scenarios.
Clean code?
I try to use as much tools I have available to make sure my code is good enough. Let’s not forget PHP is interpreted and there may be some bugs that will only raise when some scenarios are executed.
Which tools I am refering to?:
- PHP 7 new features like:
- Strict typing.
- Typed class members.
- Typed arguments to functions.
- I also use PhpStorm tools as much as possible:
- On line code checking.
- Code inspections.
I am not happy until PhpStorm informs me that all my files are ok.
This strategy has one drawback: sometimes PhpStorm is a bit ‘special’ and I need to write some additional phpdocs to make it happy.
My criteria is:
- I don’t write any phpdoc unless it is necessary.
- I only write them to make Phpstorm happy.
Inheritance, really?
Well… I agree that we should try to avoid inheritance and use some alternative design patterns instead (like composition or decoration). I also find PHP traits very useful in these situations.
But I try to be pragmatic: if I need some feature and inheritance can help me to implement it efficiently and without any further undesired side effects , I will use it.
For example: all my structural classes (Board
, Cell
, Row
, etc) inherit from an abstract class Entity
. It has allowed me all these classes share some common behavior (like event publishing). And it is just first level inheritance. So I think the trade-off is good.
Managing events
At first, I didn’t think I would need any kind of events for my application. But when I reached my Strategy classes I realized they might be very useful:
- To debug the application (how the strategies are working in their inside).
- To return the user a list of all performed steps.
So I decided to implement events.
To do this:
- I implemented a new class
Domain\Sudoku\Entity\EventBus
(I decided the event bus is an entity of my domain). - I linked it to my parent class
Domain\Sudoku\Entity\Entity
as a static member. - I created a new static public method
subscribe
so any object can subscribe to the event bus. - I created a new protected method
publish
so any class inheriting fromEntity
can publish into the event bus. - Usually, the use case level class will be the only one to subscribe to the event bus. But I have also subscribed to the event bus in my phpunit classes to debug them.
Which events do I publish into the bus? So far, the only class publishing events is Cell
. It publishes whenever its content is modified.
Logging
In my personal opinion. Logging is not the same as event publishing. I usually log any information useful to check from the outside what’s happening inside my application as easy as possible.
My first design didn’t have logging. But when I reached the console command Infrastructure\Command\SolveSudoku
and I started running it from the command prompt, I realized I wanted some logs.
The only class I wanted logging is Domain\Sudoku\Application\Handler\SolveSudoku
(this is the class responsible to implement the use case). So I decided to inject the logger from the calling class (Infrastructure\Command\SolveSudoku
).
As this logger object is optional, I needed to check if it is a valid one before logging anything. To do so, if the logger object is not valid, I use a dummy logger.
Conclusions?
I have enjoyed a lot implementing this application. I has given me the opportunity to think about and to practice new ways of coding.
It’s possible you don’t agree with my conclusions. It’s ok, and I would love to read your feedback!!!!