OOP - where to declare properties?

My parent class has several methods that rely on properties that can only be set by child classes. What is the β€œbest” approach to achieve this?

  • The parent class has the default properties (for example, the string is set to null), then the child class sets the parent properties in its constructor.
  • A child class defines properties as its own
  • If the methods of the parent class take the necessary values ​​as parameters, instead?
  • Something else???

Also - is it a bad OOP that parent methods rely on what the child has to provide?

I do this in PHP, if that matters.

+5
source share
4 answers

So ... You have one class that has an algorithm and another class that is responsible for providing data for this algorithm ... When I say this, it still makes sense for you for one of these classes to be the parent of the other ?

Your "parent class" should take the object of the "child class", probably as a parameter in the constructor, and then use this object to do its job. This composite picture is much more reliable, reusable and understandable.

For more information, see the strategy template in the literature on design patterns.


In other words, instead:

+--------------+ | Parent | +--------------+ |*getName() | |showGreeting()| +-----+--------+ ^ | | +----+-----+ | Child | +----------+ |*getName()| +----------+ 

Do it:

 +--------------------+ +----------+ | Parent | | Child | +--------------------+ +----------+ |Parent(child: Child)|--->| getName()| |showGreeting() | +----------+ +--------------------+ 
+8
source

The way to deal with this in PHP is the same as how you deal with it in any decent language - using protected abstract methods.

http://php.net/manual/en/language.oop5.abstract.php

 <?php abstract class AbstractClass { // Our abstract method only needs to define the required arguments abstract protected function prefixName($name); } class ConcreteClass extends AbstractClass { // Our child class may define optional arguments not in the parent signature public function prefixName($name, $separator = ".") { if ($name == "Pacman") { $prefix = "Mr"; } elseif ($name == "Pacwoman") { $prefix = "Mrs"; } else { $prefix = ""; } return "{$prefix}{$separator} {$name}"; } } $class = new ConcreteClass; echo $class->prefixName("Pacman"), "\n"; echo $class->prefixName("Pacwoman"), "\n"; ?> 
+4
source

Based on your comment here , it looks like you're talking about subtyping polymorphism . The basic idea of ​​polymorphism is that you can do something like this.

Option 1 : no by default. This should be used if calling this function should never come from a parent class (e.g. Shape)

 class Shape { // This doesn't make sense until we know what shape we're talking about abstract public function getArea(); } 

Then you have child classes that look like this:

 class Square extends Shape { public $sideLength; public function getArea() { return ($this->sideLength ** 2); } } class Triangle extends Shape { public $base; public $height; public function getArea() { return ($this->base * $this->height * 0.5); } } etc. 

Option 2 If the function should be called in the parent, you must include the properties and the function.

 class Rectangle { public $length; public $width; public function getArea() { return ($this->length * $this->width); } } 

Then you have child classes that look like this:

 class Square extends Rectangle { public $length; public function getArea() { return ($this->length ** 2); } } etc. 
+1
source

In my opinion, this is a very broad question, and the answer depends entirely on the use case. Let me show you some simple examples:

Your first suggestion:

 abstract class Vehicle { String name = "SuperDriver"; int numberOfWheels = 4; int maxSpeed = 20; public function drive(Terrain terrain) { echo this.name." drives with ".(this.maxSpeed - terrain->modifier)." mph in ".terrain->name; } } class Car extends Vehicle { public function __construct() { this.name="Ferrari"; this.numberOfWheels = 4; this.maxSpeed = 310; } } class Bicycle extends Vehicle { public function __construct() { this.name="Mountain Bike"; this.numberOfWheels = 2; this.maxSpeed = 150; } } 

Benefits:

You can even create an instance of Vehicle if you don't declare it abstract, and it can even control it.

Disadvantages:

  • An unsafe vehicle doesn't make much sense
  • Anyone who extends a class should know that all properties must be overwritten.
  • hard to expand and hard to maintain
  • maybe others ...

Your second suggestion:

 abstract class Vehicle { public function drive(Terrain terrain) { echo this.name." drives with ".(this.maxSpeed - terrain->modifier)." mph in ".terrain->name; } } class Car extends Vehicle { this.name="Ferrari"; this.numberOfWheels = 4; this.maxSpeed = 310; } class Bicycle extends Vehicle { this.name="Mountain Bike"; this.numberOfWheels = 2; this.maxSpeed = 150; } 

I do not see any advantage. If the parent class needs these properties, you must definitely make them necessary. If the parent class does not need any of these properties, then this is the territory of the child class, which you should not care about.

Your third suggestion:

 abstract class Vehicle { protected function drive(Terrain terrain, String name, int maxSpeed) { echo name." drives with ".(maxSpeed - terrain->modifier)." mph in ".terrain->name; } abstract public function drive(Terrain terrain); } class Car extends Vehicle { this.name="Ferrari"; this.numberOfWheels = 4; this.maxSpeed = 310; function drive(Terrain terrain) { parent::drive(terrain,this.name,this.maxSpeed); } } class Bicycle extends Vehicle { this.name="Mountain Bike"; this.numberOfWheels = 2; this.maxSpeed = 150; function drive(Terrain terrain) { parent::drive(terrain,this.name,this.maxSpeed); } } 

Benefits:

  • You now have the flexibility.
  • You can expand the disk method

Disadvantages:

  • Basically, you declare two different methods with the same name to achieve what you want it to be a workaround.
  • not very flexible

Here's how you could force immutable properties:

 abstract class Vehicle { String name; int numberOfWheels; int maxSpeed; public function __construct() { } protected abstract function getName(); protected abstract function getNumberOfWheels(); protected abstract function getMaxSpeed(); public function drive(Terrain terrain) { echo this.getName()." drives with ".(this.getMaxSpeed() - terrain->modifier)." mph in ".terrain->name; } } class Car extends Vehicle { protected function getName() { return "Ferrari"; } protected function getNumberOfWheels() { return 4; } protected function getMaxSpeed() { return 310; } } class Bicycle extends Vehicle { protected function getName() { return "Mountain Bike"; } protected function getNumberOfWheels() { return 2; } protected function getMaxSpeed() { return 150; } } 

If you do this like this, no one can change the properties outside, but only subclasses.

You can also use the protected constructor in the superclass:

 abstract class Vehicle { String name; int numberOfWheels; int maxSpeed; protected function __construct($name,$numberOfWheels,$maxSpeed) { this.name = name; this.numberOfWheels = numberOfWheels; this.maxSpeed = maxSpeed; } public function drive(Terrain terrain) { echo this.name." drives with ".(this.maxSpeed - terrain->modifier)." mph in ".terrain->name; } } class Car extends Vehicle { // or pass the arguments here, if you want an outside user to set em public function __construct() { parent::__construct("Ferrari",4,310); } } 

Why does it work:

Note. Parent constructors are not called implicitly if the child class defines the constructor. To start the parent constructor, call parent :: __ construct () inside the child constructor. If child does not define a constructor, then it can be inherited from the parent class, as a regular class method (if it was not declared as private).

If you have public properties that just need to be initialized, just use the constructor arguments.

Your second question: Is it bad OOP that parent methods rely on what the child has to provide?

In my opinion, no, not necessarily. Otherwise, there would be only interfaces, not abstract classes or, for example, Traits (PHP). Mechanisms like this avoid code duplication .

0
source

Source: https://habr.com/ru/post/1269163/


All Articles