For instance, I believe experienced developers create database accessor classes, and application classes and maybe getting into the whole MVC thing which I know little about
Well database classes, even basic ones are handy as they allow the error handling to to be wrapped and hidden. You just have to look on forums to see the amount of template based code using the
if( $rs = mysql_query( $sql ) ){
while( $row = mysql_fetch_row( $sql ) ){
echo $row['dancing_monkey_id' );
}
}else{
trigger_error( "Something bad happened", E_USER_ERROR );
}
method everywhere..
The problems with this are
1. Error handling is hard coded to output to the screen multiple times throughout the code base.
2. Some query issues happen intermittedley( for example if using is_numeric then hex numbers pass that check but the database doesn't like it ). These errors are only evident when stuff like url tampering occurs etc, not from normal use. Being alerted there is a hole in your design when you are not the one using that page is pretty handie is it points to where you need to be tidy things up. No-one is perfect.
3. Sometimes indexes/tables need to be repaired so knowing about this ASAP is pretty important.
4. It contains multiple level of knowledge.
a. What database it is using and that it is using a database
b. What that data is going to be ultimately used for
c. What to do if it goes wrong ( again error handling stuff but bad error handling is a pain in the rear, even people well respected in the OOP community with a lot of experience create stuff that just does not behave well on a fail and means digging through source code adding debugging to work out usage problems that should be chrystal clear to resolve by being clearly informative ). Problems seen include autoloaders that do not error when they cannot load causing the fail to be miles away and mock object code that errors in an eval error if it is not set up in a certain way. Seeing error on line x when I cannot see the code without digging through the whole framework is not fair.
Eg. If it is your local development machine get it to show the error on the screen or email and log if public is quite a nice simple easy way to work. By abstracting the core database interaction away these design decisions can be changed in one place very easily. Change is always nice if it has to be made in the smallest of places and it is easy to find where those places are.
Data accessor classes( classes that obtain data via the data abstraction class ) are also very easy to write, do your database design and then site down and concentrate on those you need to utilise soonest. Do too many though and risk flaws in database design causing a lot of chnage while fixing it. You'll probably find you get a rhythm going as your database design is a clear reminder of what you were trying to achieve with the design( if done visually and relationally ). Keeping all the sql regionalised also makes it easier to future reference how to do things and how you did things. Hunting through unrelated business logic to find the query becomes tiring and it is also harder to absorb due to the outer distractions that that scream look at me all the time. Code blindness.
For design I have written a case study in the process of working through the section of a code base dealing with images.
You could have one generic ImageManager class to handle all things to do with images( locating, resizing etc ) but the clue in it's loose design is in its name. The name does not indicate any specifics when it comes to type of action. If it where specific to resizing and locating then it would be ImageLocaterAndResizer, this makes it clear that there are multiple responsibilities.
lets examing these three responsibilites and examine their conjoining( domain specific here means tied directly to your project )
1. ImageResizer
a. It has cross project functionality, the name says it will resize images and it doesn't care what for. It is not ShopImageResizer which is domain specific
b. It is tied heavily to php internal functionality. Meaning reason for change may be out of your control, having to go from GD2 to ImageMagick may be a hosting issue etc.
c. The code to do this stuff can be pretty nasty looking and ugliness should be locked away in a tight little box. Work it out once and hide. The eyes are precious 😉
d. Note the action part of the name, it says resizer, without looking in tin seeing that being used some where it is clear that it is is resizing.
e. It is dumb as can be, dumb=simple. Simple is good.
f. The object has no dependencies. All it takes is source location, target location and image specific stuff like dimensions.
g. Public methods are comprised of resizeJpeg( $sourceImageLocation, $newWidth, $newHeight ), resizeJpegToNewLocation( $sourceImageLocation, $newImageLocation, $newWidth, $newHeight ) etc. You also notice that I chose two methods rather than 1 with an optional parameter to indicate change of action eg. resizeJpegToSameLocation( $sourceImageLocation, $newWidth, $newHeight, $targetLocation = null ). The reasons for this simply involve being left to right readers information held furthest to the left is found first. They are written in an english format so it is easier to understand for people who don't want to be Neo from the matrix owning the codes. Optional parameters, like out parameters( out parameters are much worse for this ) obscure side behaviour when reading. Adding a global function, unlike a method has a polluting effect in the global namespace so things like optional parameters are more favoured there. Out parameters as an additional way to return values has different reasons but again more favoured in procedural programming. Just because someone wraps something in a class does not mean it is OOP and that approach only scales so far until it is eye burning for the next person or you in a few years 🙂. Can give job security though.
ShopImageLocationBuilder
a. It is very domain specific as it's behaviour and reason for change are related to system organisation.
b. Again it is dumb as can be. All it knows about is file location management.
c. It has no dependencies
d. It really is just a glamorised string builder, public methods are nice and simple and are simple called locateItemImage, locateCategoryImage etc.
ShopImageStorer ( it is hard coming up with the action bit of class names when forced to be specific, not very happy with storer but a thesaurus can help in these cases )
a. This is where the conjoining of the capabilities of the other two can happen without that knowledge being heaviliy exposed to its internals.
b. It is domain specific to your project, it is not cross project re-usable. This has benefits that things like using a const for image size as a parameter is not such a big no-no in my book.
c. It has two dependencies for the conjoining, ImageResizer and ShopImageLocator. The code is pretty simple. One method call acts as a parameter value for the other.
d. Public methods are project specific. You may hypothecially add resizePng to ImageManipulator but that publicity never has to be added to storing images.
Later down the line image converting may be desired and ImageTypeConvertor can be written. Naming ImageResizer ImageResizer and not ImageManipulator has made it not inviting to add this code there. Code also grows by invitation, working with other people this has taught me this is not always a good thing. 2 parameters has become 10 the next time I looked. A nice simple class of 100 lines and 140 lines of unit test has becomes a complex class of 400 lines and 1000 lines + of unit tests with a lot of decsions that have to be read even if they are not part of the current objective. Looking at stuff you don't need to costs and makes some people give up ever improving it.
The coe design when laid out could look like this. It skips polymorphism/ parent classes/ factories and just displays a simple divide and conquer approach. Each part if you wished to unit test would be very very easy to do. But that is another skill for another day 🙂
--- end of part 1