Jump to content

Why use inheritance


dennis-fedco

Recommended Posts

I have

class MyProductClass
{
    function computeProductA()
    {
        doSometing();
    }

    function computeProductB()
    {
        doSometingElse();
        $this->computeProductCommon();
    }

    function computeProductC()
    {
        $this->computeProductCommon();
    }

    function computeProductCommon()
    {
    }
}

And I was wondering ... does it make sense to move out product-specific functions into their own classes and then have them extend MyProductClass.

While I think it makes sense to me from an aesthetic point of view, and that I get to use object oriented principles, I could not answer myself as to "Why" I would do that.

 

So, why?  Why would I do that, and should I?

 

Right now in the code, various product specific classes instantiate MyProductClass and just use their specific and generic functions, as needed.

Edited by dennis-fedco
Link to comment
Share on other sites

Do you have common things you need to do to all products?  For instance, computeProduct, orderProduct, buildProduct, etc?  By creating a separate class for each product which extends a generic product, your outer application does not need to worry about the specific method names of each product type, and your individual product classes do not need to duplicate the script in the generic product class.

 

Whether you should do so is based on your specific requirements, and there is no absolute answer, however, I expect you should do so.  Also, you might want to look into factory method pattern.

Link to comment
Share on other sites

I do have common things among products, not neccessarily all of them.   There are groups and subgroups of products that need specific computations.  So in a sense methods I have in the class are like a library.  A specific method, depending on the product may call the same function for product A and B, for example.

 

I have "generic" methods right now that have

if ($product == "A")
{

}
else if $product == "B")
{

}

and I have ones that do not use product destinction, and just do a small computations that may be be called for several different products.

 

I don't see where to put factory method or why it is needed .. Are you saying I can use it to hide the fact that I may be instantiating different products?... As in, I have {x, y, z}, give me a Product that I can use generically?

There is no globally common functions between all products, but  there are groups of them.  i.e. {A, B, C} may use computations for X, and Y, but products {C, D, E} may use computation functions for Z and W.

Link to comment
Share on other sites

If that's the case, you can use multiple levels of inheritance. For instance, create the abstract Product() class, extend that in the ProductGroupA() class, then extend that for each product in group A. Do the same for group B, C, etc. You'll create the new middle class based on the similar functionality. Because everything is extending from the base Product() superclass, you can use that as a type hint in your code and everything will work. If I'm not mistaken in the labels, it's kind of a combination of both factory and strategy patterns.

Link to comment
Share on other sites

I am more so asking why use inheritance (in my case).

 

I know I can use it and I have an idea of what I want it to be like, but the "why" part is hazy to me.  I don't want to use it just because it's cool to do so, or just because it's "object oriented" if I do, or just because it's the craze right now.

Link to comment
Share on other sites

Here's a practical reason:

 

What if you had 20 products? You'd have (up to) 20 different "computeProductX" methods. And that's just for computeProduct. Another method to customize means another 20 methods. Now you've got 40 methods to sift through when looking at the code. Do you really want to do that?

Inheritance lets you do the same thing you're doing now but keeps the code a lot simpler. Each subclass has only the differences it needs. When you want to find how product M computes its whatever, you don't have to sift through dozens or even hundreds of methods to find the one you want, and instead you go to the M class and look at its computeProduct method.

Link to comment
Share on other sites

And, in all honesty, you don't have to use inheritance. A composition-style setup would be doable as well. Granted, it might take a bit more thought and work at the outset, but it would be less coupled and potentially easier to modify if the need arises.

Link to comment
Share on other sites

You don't necessarily need inheritance, but what you desparately need is polymorphism.

 

Stuffing all methods of all product variants into a single class and using gigantic if statements to pick the right method whenever you want to do something with a product is an awful approach. It obviously leads to code clutter par excellence, and if you want to add a new variant, you have to go through your entire project and adjust all if statements – hoping that you won't overlook any.

 

This is not how programming works. It's not just bad OOP code. It's bad code in general and shows a lack of abstraction.

 

Think about it: Does the user of a product really need to know every single variant with its specific computation method? Why not leave the computation to the product? The user just asks for the result, and then the current variant (whatever it is) will take the necessary steps.

 

If you want to drive a car, you don't need to know every single car model and how it works internally. You just turn the ignition and leave the rest to the car. It's the same thing with code: As a user, you want an interface for a product. This interface provides various product funitionalities like computation, getting the model code etc. How exactly those functionalities are implemented is entirely up to the variant. The user doesn't care.

 

Like I said above, this doesn't necessarily lead to inheritance. If the products don't share anything except the API, then you want an interface:

<?php

interface Product
{

	public function compute();

	public function getModelCode();
	
}

class ProductA implements Product
{

	public function compute()
	{
		return 1 + 1;
	}
	
	public function getModelCode()
	{
		return 'AAA';
	}

}

class ProductB implements Product
{

	public function compute()
	{
		return 2 + 2;
	}
	
	public function getModelCode()
	{
		return 'BBB';
	}

}

Now the user of a product only has to call the API functions without having to distinguish between the different variants:

$model_code = $product->getModelCode();

Which getModelCode() method of which class this is doesn't matter. We just want the model code, and the variant knows how to calculate it.

Edited by Jacques1
  • Like 1
Link to comment
Share on other sites

And, in all honesty, you don't have to use inheritance. A composition-style setup would be doable as well.

I don't immediately see how that will work. .. Maybe because of my specific use case, it didn't make sense, or maybe I am not seeing how it could be used.

 

My products are different enough where I think polymorphism will work a little cleaner.  With composition, I think I will just have mostly disjoint composites.

soo .. say model code ... maybe a couple of the products share the same model code, but the rest are different.  How would I code that?  What would I call it?  If ProductA and ProductB use the same model code, but Product C through F are all different, what would I do?

Link to comment
Share on other sites

If product A and product B have a lot in common, it might make sense to create a subclass of product for them. For example: Product is the root class, below that are FooProduct (which in turn is the parent of ProductA and ProductB) and BarProduct (which is the parent of C, …, F).

 

Another approach is to use traits. Unlike inheritance, this does not create a strict hiarchical structure. A trait is basically a method container which can be mixed into arbitrary classes. So ProductA and ProductB would have Product as their parent, but they'd get their model code method from a trait.

Link to comment
Share on other sites

I haven't delved into traits too much yet, so I can't speak to that, but composition could work in much the same way. Instead of inheriting from a common Product parent class, though, the disparate child classes would implement a Product interface. Then you can use Product as a type hint, and the base class still couldn't care less what specific product it's working with or how it works, as long as the product classes are coded to the interface's contract. So all products have a getPrice() method defined in the interface, but ProductA and ProductB return a straight number while ProductC, ProductD, and ProductE all perform complicated mathematical equations to arrive at their prices. The using class doesn't care, 'cause it knows it can simply call $this->product->getPrice() and $this->product will do whatever it needs to and return the price.

 

That being said, you could combine this idea with the inheritance pattern described by Jacques1 quite easily to corral any common functionality between product suites as necessary and override and/or expand the base product functionality in some or all of the leaf classes.

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.