Quantcast
Channel: Obsessed with Oracle PL/SQL
Viewing all articles
Browse latest Browse all 312

Dynamic Polymorphism - Why, What, How

$
0
0
Dynamic means "run-time."

Polymorphism means "multiple shapes."

Synonyms for dynamic polymorphism include "runtime polymorphism" and "dynamic method dispatch."

If you are a "traditional" relational database developer, these terms might sound unfamiliar. But how about overloading? Are you familiar with that?

Overloading occurs when...

Well, guess what? Another name for overloading is "static polymorphism."

Static means "compile-time."

Polymorphism means "multiple shapes."

Why, you might be wondering, does the Oracle Database need to wait till runtime to determine which method in which type in the hierarchy should be called?

After all, it doesn't have any troubling sorting that out with overloading of subprograms in packages!

The answer to that question lies in one word: substitutability.

It's a topic I've touched on both directly and indirectly in my previous posts in this series. The best way to think about substitutability is that if I have the following type hierarchy....
CREATE TYPE food_ot AS OBJECT (
name VARCHAR2(100),
food_group VARCHAR2 (50),
grown_in VARCHAR2 (100)
)
NOT FINAL
;

CREATE TYPE dessert_t UNDER food_ot (
contains_chocolate VARCHAR2(1),
year_created NUMBER(4)
)
NOT FINAL
;
....then this follows:
Every dessert is a food, but not every item of food is a dessert.
And now with the S word:
Where I have an instance of food, I can substitute it with an instance of dessert.
This ability to substitute is precisely what drives the need for dynamic polymorphism in object-oriented languages. Let's find out why.

Since polymorphism has to do with choosing the right method, let's enhance my food-related hierarchy to include a member method in each type.
CREATE TYPE food_ot AS OBJECT (
name VARCHAR2 (100),
food_group VARCHAR2 (100),
grown_in VARCHAR2 (100),
MEMBER FUNCTION price
RETURN NUMBER
)
NOT FINAL;
/

CREATE OR REPLACE TYPE BODY food_ot
IS
MEMBER FUNCTION price
RETURN NUMBER
IS
BEGIN
RETURN (CASE self.food_group
WHEN 'PROTEIN' THEN 3
WHEN 'FRUIT' THEN 2
WHEN 'VEGETABLE' THEN 1
END);
END;
END;
/

CREATE TYPE dessert_ot
UNDER food_ot (
contains_chocolate VARCHAR2 (1),
year_created NUMBER (4),
OVERRIDING MEMBER FUNCTION price
RETURN NUMBER
)
NOT FINAL;
/

CREATE OR REPLACE TYPE BODY dessert_ot
IS
OVERRIDING MEMBER FUNCTION price
RETURN NUMBER
IS
multiplier NUMBER := 1;
BEGIN
RETURN 100;
END;
END;
/
I'm keeping the price formula really simple for desserts. :-)

And now consider the following block:
DECLARE
TYPE foodstuffs_nt IS TABLE OF food_ot;

fridge_contents foodstuffs_nt
:= foodstuffs_nt (food_ot ('Brussels Sprouts', 'VEGETABLE', 'Farm'),
dessert_ot ('Strawberries',
'FRUIT',
'Backyard',
'N',
2001));
BEGIN
FOR indx IN fridge_contents.FIRST .. fridge_contents.LAST
LOOP
DBMS_OUTPUT.put (
CASE
WHEN fridge_contents (indx) IS OF (ONLY food_ot)
THEN
'Food'
WHEN fridge_contents (indx) IS OF (ONLY dessert_ot)
THEN
'Dessert'
END
|| ' price:');

DBMS_OUTPUT.put_line (fridge_contents (indx).price ());
END LOOP;
END;
/
We can see from this code why overloading or static polymorphism is not sufficient when it comes to executing the right method.

When the block is compiled, the PL/SQL engine knows that the fridge_contents nested table is filled with instances of type food_t. It could even, I suppose, notice that the nested table contains instances of food_t and dessert_t.

But it sure is hard to see how at compile time. the PL/SQL engine would know which price method should be used in the call to DBMS_OUTPUT.PUT_LINE. After all, fridge_contents (indx) at compile time is of type food_t (and, of course, because of substitutability, it could also be any subtype of food_t, but what compiler could sort that out?).

It is only when the block is executed that PL/SQL can check to see the actual type of the instance in that element of the collection and invoke the appropriate method.

And as you can see from my use of the IS OF syntax, it is possible for both us and the PL/SQL engine to get that type.

You can run this code for yourself on LiveSQL.

Check Out the Entire Series

Visit this post that gives you quick access to all the articles in the series.



Viewing all articles
Browse latest Browse all 312

Trending Articles