4 ms·
Well, I can't speak necessarily for the way you write or view code, but for my process I've found its impact intrinsic and clearly definable. First I should no
by j42 11y ago
Well, I can't speak necessarily for the way you write or view code, but for my process I've found its impact intrinsic and clearly definable.
First I should note that I follow a few basic principles/sets of principles when programming loosely-typed imperative languages.
1) Single-responsibility for classes and methods, with each method coming in around <= 20 lines. In total the majority of my classes in enterprise level applications (supporting 100's of millions of users & responding to internal events with meta/statistical analysis) rarely grow beyond 200 lines per class. Usually whenever I hit the 2-500 line range I'll find that many methods can be logically abstracted to a few core traits in order to take advantage of multiple-inheritances without overcomplicating the central registrar or DI patterns.
2) I build an interface before building a class--the interface defines what methods will be available to the application/world context (thinking as per an API interface), as well as what type(s) should be received and what type(s) should be returned. By type hinting/checking at this stage and ensuring conformance to an interface, you can swap out implementations easily later, as well as have a general map or "spec" before you really start hammering the nails--this helps in staying organize and weeding out bad architecture decisions early.
3) I keep these public methods simple, so that I can clearly detect failure points and debug based on input/return types for the 'service' as a whole, and then I use protected methods internally similarly to data pipes in functional programming; each method is clearly named, has a specific transformation it applies, and acts on a series of (n) objects by mapping transformations vs iterative loops which precludes un-terminated conditionals and other type-juggling weirdness.
This all results in software that's very concise (IMHO), runs well, and is quite simple to test. I can inherit any class from a testclass, in order to test the protected data pipes with any sample streams. I can verify the I/O types for all interfaced methods of the class (via functional tests), and tie it all together neatly in knowing that I can pull up any file and clearly differentiate between what formats/returns/transports data, and what mutates it and/or the "state."
The process is by no means perfect and I continue to learn in my pursuits as do we all, however this structure has worked well for me consistently on the types of large projects where others have failed, and in that context I feel it's worth expounding.