Organization of classes in PHP

Let's assume I have the following classes in my project:

  • class Is // a validation class
  • class Mathematics // class for manipulating numbers

Now if I want to check 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, I slow down my thinking process, and I often make mistakes. 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 latter. Lacking a standard, I feel like the whole codebase is dirty.

Is there a holy grail solution for organizing classes? Maybe a different paradigm?

+2


source to share


8 answers


I don't think this is ambiguous. "Is" is the first in each of these examples, and I'll tell you why: "Is" is a superset of validation operations in which Is :: Math is a member.

In the case of Is :: Math, what are you doing? Do you do mathematical operations? Or are you checking math objects? The latter is obvious, otherwise it's just "math".



Which of these two operations has the highest volume? Is an? Or math? Obviously this is because Is is conceptually applicable to many objects other than Math, whereas mathematics is mathematical. (Similarly, in the case of Math :: Factor, it will not be Factor :: Math, because Math is the superset where the factor belongs.)

The whole purpose of this type of OOPing is to group things in a way that makes sense. Validation functions, even when applied to completely different entity types (primes and PNG images), are more similar to each other than to the ones they compare. They will return the same data types and are called in situations of the same type.

+3


source


For the Math example, I would put the actual functionality of checking if a number is prime in a class Math

. In the class, Is

you must place a method that will be called when the validation should happen. Then you would use Math::Prime()

.

C Image

, this is a type check. You probably don't need to create a method unless you are sure that valid image data is loaded.



Using the method PNG

, same with Math

. Put the real PNG data validation algorithm in Image

and make your validator method in Is

it.

The sample zip code should be in your class Is

just because it works with a string primitive and will probably just use a regex (read: it won't be a tricky method, unlike your PNG validation, which it probably will).

+10


source


If you want to respect SRP ( http://en.wikipedia.org/wiki/Single_responsibility_principle ), do a little exercise:

Pick 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 a different class.

See page 36: http://misko.hevery.com/attachments/Guide-Writing%20Testable%20Code.pdf

Another right (there are many more) to help you organize your classes: Demeter's Law ( http://en.wikipedia.org/wiki/Law_of_Demeter ).

To learn a lot and help you make the right choice, I advise you to blog Misko (google evangelist): http://misko.hevery.com

Hope it helps.

+4


source


Anything about handling the validation in itself will match your Is

-classes:

  • Has it gone?
  • What parts didn't go through?
  • Should validation errors be logged somewhere?

Zend_Validate in Zend Framework provides such an approach, you might get some inspiration from it. Since this approach will allow you to implement the same interface in all validation classes, you can easily

  • use the same syntax for validation, regardless of which data is validated
  • it's easy to recognize what validation rules you have by checking for all named classes Is_Prime

    , Is_Image

    instead of checking for Math_Is

    , Image_Is

    everywhere.

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 thus 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);
        }
    }
}

      

+3


source


I think there is no "right answer" for the problem you mentioned. Some people will put Prime in Is and some will put Math. There is ambiguity. Otherwise, you won't be asking this question.

Now you need to somehow resolve the ambiguity. You can think of some rules and conventions that will tell which class / method goes there. But this can be fragile as the rules are not always obvious and they can get very complex, at which point they no longer help.

I suggest you create classes to make it obvious when you look at the names where some method should go.

Do not include your verification package. It's such a generic name that almost everything goes there. IsFile, IsImage, IsLocked, IsAvailable, IsFull - doesn't sound good, okay? There is no cohesion in this design .

It is probably best to do the component filter data at the subsystem boundary (where you need to enforce security and business rules), nothing more.

After making this decision, your example will become obvious. Prime belongs to mathematics. Is :: The image is probably too generic. I would prefer Image :: IsValid because you will probably have other methods of working with the image as well (more cohesion). Otherwise "Is" becomes a bag for everything , as I said at the beginning.

+1


source


I don't think "is" at all belongs to class names. I think 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' );

      

+1


source


Is there a holy grail solution for organizing classes? Maybe a different paradigm?

No, this is the main drawback of the oop class. This is subjective.

Functional programming (Not to be confused with procedural programming) has fewer problems with this question, mainly because the basic building blocks are much smaller. Classless oop also performs better, being a hybrid of oop and functional programming.

0


source


Classes can be thought of as fancy types that do things like check 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 are extending ValidatingType to create a validation type. This obliges us to create the 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 of the benefits of this approach is that you can keep creating new validation types, and you don't get the Is popup class.

There are more elegant options in this approach. This is a simple variation. The syntax may need to be cleaned up.

0


source







All Articles