Why do PHP programmers create abstract classes

Sense of object orientation

The point of object orientation is not to create faster programs, but to accelerate their development and, above all, to ensure their expandability.

Inheritance is an important issue here, and abstract classes are a wonderful way to program a general approach that many heirs can then adapt to suit their needs. This, and for example access protection, was not yet supported by PHP 4. That is not to say that it was not possible, for example, to make something like an abstract class of which a method was overridden by an heir, but PHP did not warn, for example, if an heir forgot to override that method.

Fortunately, PHP 5 supports a lot of the language. For example, the cumbersome and often error-prone rummaging around with references is no longer necessary. In PHP 5, as in Java, objects are simply no longer copied, but referenced.


Access control to methods and properties

One of the most side-effect-free concepts in the computer world is the black box principle, according to which one class does not have to know how the other works. In order to prevent, for example, another programmer from accessing functions or properties that affect the internal functioning of the class, access can be restricted to functions that belong to the class (private).

In this example, the methods are public and the properties are private. While there is nothing wrong with private methods, there are a few reasons against public properties:
  • Every access, whether reading or writing, to a variable can be covered by a function that receives a parameter or supplies a return value.
  • The data type in which the variable is saved becomes less important the more abstract the access is. A setNumResults (55); works fine with integers and floating point numbers, in untyped languages ​​like PHP also with strings, a setReadOnly (); can change a boolean. But it can also set an integer to a certain value or carry out other actions.
  • There are just other things that can be done that need to be done when a variable has been changed. It can be the case that the value of another variable has to be adjusted if a variable changes. Private methods support this by making the variable to be changed private and only offering a get function for read-only access, and changing this variable in the set function of the writable variable. Changes to these actions are now made centrally. A set function, which only consists of the assignment of the parameter to the class variable, ensures that any dependent variables can later be changed simply by adding their update to this set function.
  • Read-only or write-only access can be implemented, for example, by offering a setReadOnly function, but not a getReadOnly function. Certain conditions, such as those used internally, remain in the class.

There are also other keywords: In addition to public and private, my favorite is "protected", which allows access to its own class and its heirs.


Abstract classes

Abstract classes are not instantiated, they just serve to define common methods and properties that will later be used by a number of similar heirs.

For many things that can be classified, one could use the classification as an abstract class. Elephants, penguins and humans are animals, but there is no animal that is simply an animal and does not belong to any species.

Abstract classes are so beautiful because they do not even implement the things that distinguish the heirs, but can more or less say: Each of my heirs has to implement this method for himself, but I cannot say how that with my heirs looks in detail.

getNumberFeets (). "feet and". $ human-> getCommunicationMethod (). ".

"; $ dog = new cDog (); echo "dogs have". $ dog-> getNumberFeets (). "feet and". $ dog-> getCommunicationMethod (). ".

"; ?> The example is not that exciting yet, because abstract classes save more work, the more non-abstract functionality is declared in them and the more heirs they have, which differ in as few functions as possible (abstract in the original class). With 50% abstract functions and only two heirs, they are not really showing their strengths.

Interfaces

A pretty extreme approach to abstract functions are interfaces. All functions are abstract in interfaces. Like Java, PHP is a language that does not support multiple inheritance, and like Java, this principle is being weakened for interfaces. A class can inherit from a parent class and any number of interfaces.

The following example explains why multiple inheritance is problematic: Both rectangle and triangle inherit from class shape and overwrite the getArea method on this occasion. If you come up with the idea of ​​defining a rectangle with an attached triangle as the inheritance of both classes, you would either have to specify for its getArea method which of the parent classes should be used or incorporate a warning that in this case getArea will be implemented in the RectangleWithTriangle class got to. Calling both father methods is basically out of the question, because the order of the call can be decisive for the result and you do not know what to do with possible return values.

In Java, for example, interfaces are often used for functions that allow user access. For example, an interface can contain a virtual function that defines how a key is handled. You can then expect certain classes to implement certain interfaces and call the corresponding virtual functions from the base class.

In hard-typed Java this makes sense, in weakly typed PHP, where you can expect a certain class (at runtime) at most through hints, this is problematic. So an interface is something like a set of abstract functions.

The original example from the Zend 2 announcement

interface Throwable {public function getMessage (); } class MyException implements Throwable {public function getMessage () {// ...}}

Copy objects

Since PHP version 5 and higher works with objects with references, one must somehow be able to explicitly create a copy of an object:

By the way, the example also shows a nice way to convert objects. At least in strings, with an "overloaded" (i.e. overwritten) __toString function, the return value of which is automatically used if the object as a whole is to be converted into a string.

  • Exceptions
  • Exceptions are error codes in the object-oriented paradigm, they are particularly useful wherever
  • Error codes are poorly suited as a return value because the return type of a function does not allow this. An example is a function that can return all positive and negative numbers and the 0 in normal operation: this can hardly make it clear that an error has occurred without returning a completely different type (what should it return? -1 could also return a normal Result ...)

The nesting depth is large, i.e. errors can occur that are intended to prevent further processing of the program, in which errors can then occur. Querying these errors with "if" quickly results in a deeply nested program structure with dozen, sometimes very long "if" blocks.

Program parts must finally be executed regardless of the occurrence of errors, for example closing files.

First of all it is important to know: exceptions are thrown with throw and caught with catch. If something should be carried out independently of an exception (for example, cleaning up after work has been started), it comes in a "finally" block.

The throw looks like this: throw new Exception ("illegal handler type");
  1. Example: Error codes with a program that reads a file that contains a positive or negative number (or 0):

    $ fh = zero; If (seeIfFileExists ($ f) === true) {if (hasFileAccess ($ f) === true) {$ fh = fopen ($ f); if ($ fh) {if (fileSize ($ fh)> 0) {$ fcont = readfile ($ fh); if ($ fcont! == null) {fclose ($ fh); }} If the functions throw decent exceptions, i.e. use different exception classes, you can easily react to the type of incident by replacing the catch (Exception $ e) with} catch (fileContentException $ e) {blameFileAuthor (); echo $ e-> getMessage (); } catch (fileReadException $ e) {blameFileAuthor (); echo $ e-> getMessage (); } catch (fileSizeException $ e) {blameFileAuthor (); echo $ e-> getMessage (); } catch (fileOpenException $ e) {blameOperatingSystem (); echo $ e-> getMessage (); } catch (fileAccessException $ e) {blameOperatingSystem (); echo $ e-> getMessage (); } catch (fileExistenceException $ e) {blameUserForSelectingWrongFile (); echo $ e-> getMessage (); } As you can easily see, different categories of errors would make sense here, which can be implemented by inheriting the exception class: with a class hierarchy like Exception-> AuthorException-> fileContentException Exception-> AuthorException-> fileReadException Exception-> AuthorException- > fileSizeException Exception-> OSException-> fileOpenException Exception-> OSException-> fileAccessException you can shorten the code to} catch (AuthorException $ e) {blameFileAuthor (); echo $ e-> getMessage (); } catch (OSException $ e) {blameOperatingSystem (); echo $ e-> getMessage (); } catch (fileExistenceException $ e) {blameUserForSelectingWrongFile (); echo $ e-> getMessage (); } A few more remarks on the advantages of exceptions:

    } catch (fileContentException $ e) {blameFileAuthor (); echo $ e-> getMessage (); } Nice to mention that, since the fileContentException is received in the function where it is detected, it can also contain more detailed information than "don't know what happened, no balance returned, maybe file content not numeric".
  2. For example, the function could look like this: