best way to refactor my form (procedural to oop?) - php

(Note: this is related to this question, but I think it could have been written more clearly, so I'm trying again -- my update only helped to a limited extent.)
I've inherited some code that creates a complex form with numerous sections, and lots of possible views, depending on a number of parameters. I've been working with it for a while, and finally have a chance to think about doing some re-factoring. It's currently written procedurally, with a bunch of functions that look like this:
get_section_A ($type='foo', $mode='bar', $read_only=false, $values=array()) {
if ($this->type == 'foo') {
if ($this->mode == 'bar') { }
else { }
} else { }
}
Passing around those parameters is nasty, so I've started writing a class like this:
class MyForm {
public $type; // or maybe they'd be private or
public $mode; // I'd use getters and setters
public $read_only; // let's not get distracted by that :)
public $values;
// etc.
function __constructor ($type='foo', $mode='bar', $read_only=false, $values_array=array()) {
$this->type = $type;
// etc.
}
function get_sections () {
$result = $this->get_section_A();
$result .= $this->get_section_B();
$result .= $this->get_section_C();
}
function get_section_A() {
if ($this->type == 'foo') { }
else { }
}
function get_section_B() {}
function get_section_C() {}
// etc.
}
The problem is that the procedural functions are split into a few files (for groups of sections), and if I combine them all into a single class file, I'm looking at 2500 lines, which feels unwieldy. I've thought of a few solutions:
keep living with the nasty parameters and do something else with my time :)
live with having a 2500 line file
create a separate class for each group of sections that somehow "knows" the values of those parameters
If I do #3, I've thought of two basic approaches:
pass the MyForm object in as a single parameter
create a FormSectionGroup class with static properties that get set in MyForm, then in the group files, each class would extend FormSectionGroup and automatically have access to the current values for those parameters.
1) is probably easier to set-up, and once I'm inside get_section_A() whether I say $this->type or $myForm->type isn't all that different, but it's not exactly OOP. (In fact, I could do that without really changing to an OOP approach.)
Are there other approaches? Thoughts about which is better?

I would love nothing more than to write a lengthy explanation of how to do this, but I'm feeling a bit lazy. I do however have enough energy to point you instead to Zend_Form from the zend framework. There may be some dependencies to make it work properly (Zend_View, Elements, Decorators), but once you have them, it handles that type of situations quite gracefully.

I thought of this when I posted in your previous question - this problem reeks of decorator pattern.
It's going to be no small task, though. But I think you'll have an amazing sense of satisfaction/accomplishment once you get it done.

Having done a lot of Cocoa programming recently, I tend to view things in MVC-pattern (Model-View-Controller) terms. Hence, I'd look at the form as a controller of its various sections.
Each section object should be responsible for keeping track of its status, values and whether or not it should be displayed. Or to be precise, the section_model would take care of the values (default values, validation, etc), the section_view would take care of displaying (or not) parts of the section and the section_controller would send keep track of the status of the section and report results to the form object.
The form object should instantiate the section controllers, tell them to display or hide or whatever, and get status reports. The form object, really act a controller, can then decide when the form if completely filled out. You may have a form_model object to save the collected data, or maybe you'd rather have the section_model objects take part of that. It will take a while to get a feeling for how the different objects interact but I know from experience that if you are disciplined in designing your objects (the key is: what is an object's responsibility and what is not), you will gain a better overview and your code will be easier to upgrade. Once you find that improvements start to arise naturally, you are on the right track.

If you have the time to work on doing #3, you will probably be the happiest over the long run. Personally, I don't have the time in my job to refactor to that degree very often, and so I'd probably be forced to pick #1. #2 sounds like the worst of both worlds to me. Lots of work to get code you don't like.

Related

Testing class with high cohesion but reasonably complex logic

For example, I have a class that takes raw data, analyses it and returns numbers for a report. Let's call it SomeReportDataProvider
class SomeReportDataProvider
{
public function report(array $data)
{
$data = $this->prepare($data);
$report = [];
foreach ($data as $item) {
if ($row->somefield == 'somevalue') {
$report = $this->doThis($report, $row);
} else {
$report = $this->doThat($report, $row);
}
}
// ... do something else
$report = $this->postProcess($report);
return $report;
}
protected function doThis($item)
{
// ... do some math, probably call some other methods
}
protected function doThat($item)
{
// ... do other math
}
// ... and so on
}
So the class really does only one thing, step-by-step proccessing of raw data for some report. It's not big, most likely 4-6 methods with 5-10 lines. All of its methods are tightly related and serve one purpose, so I would say that it has high cohesion. But what's the best way to test the class?
If I try to approach it with mentality "test behavior, not implementation", I should test only it's single public method. The advantage of this approach is that it's easy to refactor the class later without even touching the tests, and I would be sure that it still exibits exactly same behavior. But it's also can be really hard to cover all cases through single method, too many possible code paths.
I can make most (probably all) methods public and test them in isolation, but then I'm breaking encapsulation of class and algorithm and testing implementation details of it. Any refactoring would be harder and unwanted changes in behavior more likely.
As a last option, I can break it into small classes, most likely having 1 or 2 methods. But is it really a good idea to break higly cohesive class into smaller, tightly coupled classes, that do very specific thing and aren't reused anywhere else? Also it's still will be more difficult to refactor. And probably harder for other developers to quickly get a full picture of how it works.
I always try to go for splitting up the classes as much as possible, like you say in your last option, because in my experience that’s the best way to simplify tests, which is by itself a very valid reason...
Also, I don't agree with you - I think this approach makes it easier to refactor in the future, and easier to understand, rather that a big class with everything together...
Looking at your code, I can see a bunch of separate “roles”: ReporterInterface, Reporter, ReportDataPreparator, ThisDoer, ThatDoer, ReportPostProcessor (obviously you can find better names :)
You might want to reuse some of them in the future, but even if that's not the case and all them are very specific to reporting, you can just put them in a separate namespace and folder (like a “reporting module”).
This reporting module has one unique API, which is your ReporterInterface, and all the rest of your system only needs to care about that interface, regardless of whether that reporter uses private methods, other classes, or a whole system in the background - they just have to call $reporter->report($data)...
So, from the perspective of the rest of the system, nothing changes, your reporting services are still all together, and your unit tests are much easier to write and maintain…

A Big Function with Case instead of many small functions

The benefits of breaking code into very small components which do one and only one simple function are obvious. But there is nothing which mandates that we should make each and every function a separate function in itself.
Consider the following case:
There is one big function, however EACH of the cases included in the function is isolated, and runs only by itself. All of the case blocks can be just copy/pasted into a different code body, wrapped in their function name. So its modular. There wont be any multiple cases(ifs) combined, ever.
All the small functions which reside in the case blocks use $vars array as their main variable. So any number of variables in any format can be passed to the parent iterator as part of an array. There are no limitations.
The parent iterator can be run anywhere, from any place, even within itself by requesting a particular action. ie $this->run_actions('close_ticket');
It has massive advantage regarding the common procedures which need to be run, and may need to be run over all actions requested. output buffering is one, and any action hooks, filters or any other all encompassing system that can be imagined.
This format allows any future new procedures which need be run before and after any action and/or on the inputs and outputs of any action, to be easily integrated. (For the particular case i have in my hands, the appearance of such cases in future is certain!!!.) If all these actions were divided into small functions instead, you would need to either go and change hooks and filters on each of the functions, or still have some sort of other function to dispatch these onto those small functions. With this format, you just place them before or after the switch/case block.
Code reading is simple: When you see a particular case, you know what it does -> 'view_tickets' is the ticket view action, and it takes $vars as an input.
Obviously, a truly hypermassive function will have various disadvantages.
So, the question is:
Assuming that the size of the function is kept at a reasonable size and the principles of modularity and one simple action per case is preserved, also considering that anyone who works with this code in future will not need to look into this code and must not modify this code and need to know only the hooks and filters which will be documented elsewhere than code itself, (including any variables they use) do you think this could be an efficient approach to combining tasks which need common procedures run on them?
public function run_actions($action,$vars=false)
{
// Global, common filters and procedures which all actions need or may need
ob_start();
switch($action)
{
case 'view_tickets':
{
// Do things
// Echo things if necessary
break;
}
case 'close_ticket':
{
// Do things
// Echo things if necessary
break;
}
case 'do_even_more_stuff':
{
// Do things
// Echo things if necessary
break;
}
// Many more cases as needed
}
// Even more common post-processing actions, filters and other stuff any action may need
$output=ob_get_clean();
return $output;
}
You can replace conditional with polymorphism. Create an abstract action class with a method like "execute" and then subclass for all various actions implementing that method.
e.g.
function run_actions(IAction action) {
//...
action->execute();
//...
}
That way, if you will need to introduce additional behavior, you won't need to modify and test long run_actions with numerous responsibilities.
Various disadvantages:
The switch cases all use $vars so they don't have a specific signature.
This hides the signature from the developer that its thus forced to read the code.
you can't do type-hinting on $vars (force parameters to be arrays, instance of some class, etc)
no IDE autocompletion
Easier to do a mistake
forget a break and you're done. No recognizable error.
Difficult to refactor
what would you do if you need to extract the code to a function? You need to duplicate preprocessing (ob_start, etc) or to change everything
what would you do if you needed to run on action with no preprocessing?
I agree it is very simple, but it has long-run disadvantages. Up to you to strike the right balance :)
When I look at this kind of architecture, I see it as beginning to build a new programming language on top of the existing one. This isn't always a bad thing, if the features you're building are a better abstraction than the language you're building them with, but it's worth challenging what those features are.
In this case, the part of the language you're reinventing is function dispatch: you have a named action, which takes arbitrary parameters, and runs arbitrary code. PHP already does this, and quite efficiently; it also has features your system lacks, such as built-in checks of the number and (to some extent) type of parameters. Furthermore, by inventing a non-standard "syntax", existing tools will not work as well - they won't recognise the actions as self-documenting structures, for instance.
The main part you gain is the ability to add pre- and post-processing around the actions. If there were no other way to achieve this, the tradeoff might be worthwhile, but luckily you have better options, e.g. putting each action into a function, and passing it as a callback to the wrapper function; or making each action an object, and using inheritance or composition to attach the pre- and post-processing without repeating it.
By wrapping the arguments in an array, you can also emulate named parameters, which PHP lacks. This can be a good idea if a function takes many parameters, some of them perhaps optional, but it does come with the drawbacks of reinventing processing that the language would normally do for you, such as applying the correct defaults, complaining on missing mandatory items, etc
There is a simple principle that says don't use more than 2 tab indentation.
eg :
public function applyRules($rules){
if($rules){
foreach($rules as $rule){
//apply ryle
}
}
}
Becomes better when you refactor it :
public function applyRules($rules){
if($rules){
$this->executeRules($rules);
}
}
private function executeRules($rules){
foreach($rules as $rule){
$rule->execute();
}
}
And this way your code will be refactored better and you could apply more unit tests than you could.
Another rule says don't use else, instead break the code eg :
public function usernameExists($username){
if($username){
return true;
}else{
return false;
}
}
Instead of doing this, you should do this :
public function usernameExists($username){
if($username){
return true;
}
return false;
}
Only this way you ensure that your code flows 100%

OOP concept understanding

I recently started learning the basics of OOP in PHP.
I am new to a whole lot of concepts.
In the traditional procedural way of doing things, if I had a repetitive task, I wrote a function and called it each time.
Since this seems to be a regular occurence, I created a small library of 5-10 functions, which I included in my procedural projects and used.
In OOP, what is the valid way of using your functions and having them accessible from all objects?
To make things closer to the real world, I created a thumbnail class, that takes an image filename as an argument and can perform some operations on it.
In procedural programming. when I had a function for creating thumbnails, I also had a function to create a random md5 string, check a given folder if said string existed, and repeat if it did, so I could generate a unique name for my thumbnails before saving them.
But if I wanted to generate another unique name for another purpose, say saving a text file, I could call that function again.
So, long story short, what is the valid OOP way to have the method randomise_and_check($filename) (and all other methods in my library) accessible from all the objects in my application?
Great question. The first thing you want to do is identify the primary objects you will be working with. An easy way to do this is to identify all the nouns related to your project. In your example it sounds like you will be working with images and strings, from this we can create two classes which will contain related attributes (functions, member variables, etc). And as you wisely mentioned, we need to ensure that the algorithms you are converting into OOP can be called from any context, so we try to keep them abstract as possible (within reason).
So for your specific situation I would suggest something like:
// Good object reference, abstract enough to cover any type of image
// But specific enough to provide semantic API calls
class Image
{
// Using your example, but to ensure you follow the DRY principle
// (Don't repeat yourself) this method should be broken up into two
// separate methods
public static function randomise_and_check($fileUri)
{
// Your code here
....
// Example of call to another class from within this class
$hash = String::generateHash();
}
}
// Very abstract, but allows this class to grow over time, by adding more
// string related methods
class String
{
public static function generateHash()
{
return md5(rand());
}
}
// Calling code example
$imageStats = Image::radomise_and_check($fileUri);
There are several other approaches and ideas that can be employed, such as whether or not to instantiate objects, or whether we should create a parent class from which we can extend, but these concepts will become evident over time and with practice. I think the code snippet provided should give you a good idea what you can do to make the jump from procedural to OOP. And, as always, don't forget to read the docs for more info.
-- Update --
Adding an OOP example:
class Image
{
protected $sourceUri;
public function setSourceUri($sourceUri)
{
$this->sourceUri = $sourceUri;
}
public function generateThumb()
{
return YourGenerator::resize($this->getSourceUri);
}
}
$image = new Image();
$image->setSourceUri($imageUri);
$thumbnail = $image->generateThumbnail();
The way I see it, you have two options:
Don't worry about cramming yourself into OOP and just make them standard, global functions in some utilities.php file you include wherever you want to use it. This is my preferred method.
If you take the more OOP approach, you could make them static functions ("methods") in some utilities class. From the PHP documentation:
<?php
class Foo {
public static function aStaticMethod() {
// ...
}
}
Foo::aStaticMethod();
$classname = 'Foo';
$classname::aStaticMethod(); // As of PHP 5.3.0
?>
Create an (abstract) Util-class with static functions:
example from my Util class:
abstract Class Util{
public static function dump($object){
echo '<pre class=\"dump\">' . print_r($object, true) . '</pre>';
}
}
How to use:
<?
$object = new Whatever();
//what's in the object?
Util::dump($object);
?>
For a beginner, OOP development is not all that different from procedural (once you master the basic concepts it gets quite a bit different, but that's not important to learning the basics).
You deal in OO concepts all the time, you just don't realize it. When you click on a file in your file manager, and manipulate that file.. you're using Object Oriented concepts. The file has attributes (size, type, read-only, etc..) and things you can do with it (open, copy, delete).
You just apply those concepts to development by creating objects that have properties and things you can do with it (methods).
In the OOP world, you don't typically make things available to everything else. OOP is all about "encapsulation", which is limiting access to only that which is needed. Why would you make a "haircut" method available to an orange juice object? You wouldn't. You only make the "haircut" method available to objects that need haircuts.
Writing reusable OO software is very difficult. Even professionals can't get it right a lot of the time. It requires a mixture of experience, training, practice, and frankly luck in some cases.
You should read about Dependency Injection as it seems to apply to your specific problem. Basically, you have an object that depends on some abstraction, maybe the "Image Library" functionality. In your controller, you would create an instance of the "Image Library" object and inject that dependency into whatever other objects required it.
That is, you need to stop thinking on the global scope altogether. Instead, you have to compartmentalize functionailties in a sane way and tie them together. Basically, objects should only know about as little as they need to know (also look up Law of Demeter and SOLID). I reiterate, this is tough to do correctly, and most of the time you can still have an application that works beautifully even if it's done incorrectly.
If you want to be very strict about this you should apply this line of thinking to everything, but if you have a function that wraps something very simple like return isset($_POST[$key]) ? $_POST[$key] : $default; I see no real harm in creating a global function for that. You could create an HttpPost wrapper class, but that is overkill in most circumstances IMO.
The short answer: use ordinary function. OOP encourages you to think about data and associated routines, using static functions instead of ordinary does not make your program more object-oriented. Following the single programming paradigm is not practical, combine them when you see that this will make your program cleaner.

Static method get - is this bad practice?

Had a discussion with a colleague about wether this is bad practice or not. Now I can not find immediate examples of this online.
We have a lot of database object mappers and call it's functions like so
(example) - the setId method get's the row from the database and set's it to predefined propertys
class Person {
public static function get($id) {
$object = new Person;
$object->setId($id);
return $object;
}
}
Using it like this we can use simple constructions like this: (where we got the id from for-example a post)
$person = Person::get($id);
instead of
$person = new Person;
$person->setId($id);
Now, my instinct tells me this is bad practice. But I can not explain it. Maybe someone here can explain why this is, or is not bad practice
Here are some other examples how we use it. we mainly use it for getters. (just the names, not the code. Almost all of them just run a query, which can return 1 object and then use the id of the result to use the setId method)
class CatalogArticle {
public static function get($id) { }
public static function getByArticlenumber($articlenumber) {} //$articlenumber is unique in the database
public static function getRandom() {} //Runs a query returning a random row
}
This isn't horrible persay. It's an implementation of a Factory Method design pattern. It's not bad at all in principle.
However, in your specific example, it's not really doing anything significant, so I'm not so sure if it's necessary. You could eliminate the need by taking a (perhaps optional) parameter to the constructor for the id. Then anyone could call $foo = new Person($id); rather than needing an explicit factory.
But if the instantiation is complex, or you want the ability to build several different people types that can only be determined by logic, a factory method may work better. For example, let's say you need to determine the type of person to instantiate by some parameter. Then, a factory method on Person would be appropriate. The method would determine what "type" to load, and then instantiate that class.
Statics in general are hard to test and don't allow for polymorphic changes like an instance would. They also create hard dependencies between classes in the code. They are not horrible, but you should really think about it if you want to use one. An option would be to use a Builder or a Abstract Factory. That way, you create an instance of the builder/factory, and then let that instance determine how to instantiate the resulting class...
One other note. I would rename that method from Person::get() to something a little more semantically appropriate. Perhaps Person::getInstance() or something else appropriate.
This blog post should tell you why people don't like static methods better than i could:
http://kore-nordmann.de/blog/0103_static_considered_harmful.html
The question that strikes me most about your current code snippet: Is a Person allowed to NOT have an Id ?
I feel like that should be an constructor argument if it's representing a real Person. If you use that class to create new persons that ofc might not work.
The difference between the 2 calls is minor. Both "create" a Person class and set the Id so you are not winning / loosing anything there when it comes to 'hard wired dependencies'.
The advantage only shows when you want to be able to pass a Person into another object and that objects needs to change the ID (as an example, the blog post should explain that better than i did here).
I'm only adding to edorian's post, but I've used static get methods in the past, where there is a caching engine in place, and (for example) I might have a given Person object in memcache, and would rather retrieve it from the cache than going off to the database.
For example:
class Person {
public static function get($id) {
if(Cache::contains("Person", $id))
{
return Cache::get("Person", $id);
}
else
{
//fictional get_person_from_database, basically
//getting an instance of Person from a database
$object = get_person_from_database($id);
}
return $object;
}
}
In this way, all cache handling is done by the class in question, rather than the caller getting a person calls having to worry about the cache.
long story short, yes, they are bad practice:
http://r.je/static-methods-bad-practice.html
http://misko.hevery.com/2008/12/15/static-methods-are-death-to-testability/
A good reason apart of everything is that you 'should' be testing your code. Static methods cause issues, so there you have a good reason:
if you want to follow good practices, test your code
Ergo, if static causes testing issues, static prevent writing tests so it prevents to follow good practices :-)
time goes things changes.
just in case you have problems with testing you can use AspectMock library
https://github.com/Codeception/AspectMock
any way static is not so bad at all. to use static you should just know what you are doing and why. if you will place static only as fast solution it is bad idea in 99% of variations. in 1% time it is still bad solution but it gives you time when you need it.

Organizing Classes in PHP

Suppose I've the following classes in my project:
class Is // validation class
class Math // number manipulation class
Now, if I want to validate a given number for primality where would be the logical place to insert my Prime() method? I can think of the following options:
Is_Math::Prime()
Math_Is::Prime()
I hate these ambiguities, the slow down my thinking process and often induce me in errors. Some more examples:
Is::Image() or Image::Is() ?
Is_Image::PNG() or Image_Is::PNG() ?
Is_i18n_US::ZipCode() or i18n_Is_US::ZipCode() or
i18n_US_Is::ZipCode() ?
In the Image example the first choice makes more sense to me while in the i18n example I prefer the last one. Not having a standard makes me feel like the whole code base is messy.
Is there a holy grail solution for organizing classes? Maybe a different paradigm?
For the Math example, I'd put the actual functionality of checking if a number is prime in the Math class. In your Is class you would put a method that would be called when a validation needs to occur. You would then use Math::Prime() from there.
With Image, that's a type check. You probably don't need to make a method for it unless you are making sure valid image data has been uploaded.
With the PNG method, same with Math. Put the actual PNG data checker algorithm in Image and make your validator method in Is call it.
The zip code example should be in your Is class only since it operates on a string primitive and probably will just use a regexp (read: it won't be a complex method, unlike your PNG checker which probably will be).
If you want to respect the SRP (http://en.wikipedia.org/wiki/Single_responsibility_principle), do the little exercice:
Select your class and try to describe what it does/can do. If you have an "AND" in your description, you must move the method to an other class.
See page 36: http://misko.hevery.com/attachments/Guide-Writing%20Testable%20Code.pdf
Other Law (there are many more) that will help you organize your classes: Law of Demeter (http://en.wikipedia.org/wiki/Law_of_Demeter).
To learn a lot and to help you make the right choice, I advice you Misko's blog (A google evangelist): http://misko.hevery.com
Hope this helps.
I don't think it's ambiguous at all. "Is" should be first in every one of those examples, and I'll tell you why: "Is" is the superset of validation operations in which Is::Math is a member.
In the case of Is::Math, what are you doing? Are you doing math operations? Or are you validating mathematical entities? The latter, obviously, otherwise it'd just be "Math".
Which of those two operations has the greater scope? Is? Or Math? Is, obviously, because Is is conceptually applicable to many non-Math entities, whereas Math is Math specific. (Likewise in the case of Math::Factor, it wouldn't be Factor::Math, because Math is the superset in which Factor belongs.)
The whole purpose of this type of OOPing is to group things in a manner that makes sense. Validation functions, even when they apply to wildly different types of entities (Prime numbers vs. PNG images) have more similarities to each other than they do to the things they are comparing. They will return the same types of data, they are called in the same kind of situations.
Everything about handling validation in itself would fit in your Is-classes:
Did it pass?
Which parts did not pass?
Should the validation errors be logged somewhere?
Zend_Validate in Zend Framework provides such an approach, maybe you can get some inspiration from it. Since this approach would have you implementing the same interface in all validation-classes, you could easily
use the same syntax for validation, independantly of which data is validated
easily recognize which validation rules you have available by checking for all classes named Is_Prime, Is_Image instead of checking for Math_Is, Image_Is all over the place.
Edit:
Why not use a syntax like this:
class Math {
public function isPrime() {
$validation_rule = new Is_Prime();
return (bool) $validation_rule->validates($this->getValue());
}
}
And thereby also allow
class Problem {
public function solveProblem(Math $math) {
$validation_rule = new Is_Prime();
if($validation_rule->validates($math->getValue())) {
return $this->handlePrime($math);
} else {
return $this->handleNonPrime($math);
}
}
}
I think there is no "The Right Answer" to the problem you stated. Some people will put Prime in Is, and some in Math. There is ambiguity. Otherwise you wouldn't be asking this question.
Now, you have to resolve the ambiguity somehow. You can think about some rules and conventions, that would say which class/method goes where. But this may be fragile, as the rules are not always obvious, and they may become very complicated, and at that point they're no longer helpful.
I'd suggest that you design the classes so that it's obvious by looking at the names where some method should go.
Don't name your validation package Is. It's so general name that almost everything goes there. IsFile, IsImage, IsLocked, IsAvailable, IsFull - doesn't sound good, ok? There is no cohesion with that design.
It's probably better to make the validation component filter data at subsystems boundary (where you have to enforce security and business rules), nothing else.
After making that decision, your example becomes obvious. Prime belongs in Math. Is::Image is probably too general. I'd prefer Image::IsValid, because you'll probably also have other methods operating on an image (more cohesion). Otherwise "Is" becomes a bag for everything, as I said at the beginning.
I don't think "is" belongs in class names at all. I think that's for methods.
abstract class Validator {}
class Math_Validator extends Validator
{
public static function isPrime( $number )
{
// whatever
}
}
class I18N_US_Validator extends Validator
{
public static function isZipCode( $input )
{
// whatever
}
}
class Image_Validator extends Validator
{
public static function isPng( $path )
{
// whatever
}
}
Math_Validator::isPrime( 1 );
I18N_US_Validator::isZipCode( '90210' );
Image_Validator::isPng( '/path/to/image.png' );
Is there a holy grail solution for organizing classes? Maybe a different paradigm?
No, that is a basic flaw of class based oop. It's subjective.
Functional programming (Not to be confused with procedural programming) has less problems with this matter, mostly because the primary building blocks are much smaller. Classless oop also deals better, being a hybrid of oop and functional programming of sorts.
Classes can be considered to be fancy types that do things, like validating themselves.
abstract class ValidatingType
{
protected $val;
public function __construct($val)
{
if(!self::isValid($val))
{ // complain, perhaps by throwing exception
throw new Exception("No, you can't do that!");
}
$this->val = $val;
}
abstract static protected function isValid($val);
}
We extend ValidatingType to create a validating type. That obliges us to create an isValid method.
class ValidatingNumber extends ValidatingType
{
...
static protected function isValid($val)
{
return is_numeric($val);
}
}
class ValidatingPrimeNumber extends ValidatingNumber
{
/*
* If your PHP doesn't have late-binding statics, then don't make the abstract
* or overridden methods isValid() static.
*/
static protected function isValid($val)
{
return parent::isValid($val)
or self::isPrime($val); // defined separately
}
}
class ValidatingImage extends ValidatingType
{
...
static protected function isValid($val)
{
// figure it out, return boolean
}
}
One advantage of this approach is that you can continue to create new validating types, and you don't get a ballooning Is class.
There are more elegant variations on this approach. This is a simple variation. The syntax may require cleaning up.

Categories