Core points
- Subtype polymorphism in object-oriented design refers to the ability of a system to define a set of contracts or interfaces, and then implement them by different subtypes. This is crucial for designing scalable systems that can consume specific contracts without checking whether the implementer is in the expected type.
- This article demonstrates the use of subtype polymorphisms by developing an insertable cache component that can be extended to suit user needs by developing additional cache drivers.
- A key feature of the cache component is its ability to swap different cache drivers at runtime without changing any client code. This is achieved by defining a cache contract that is then followed by different implementations, thus taking advantage of the polymorphism.
- The cache component can switch backends at runtime, highlighting the importance of polymorphism in designing highly decoupled modules. This allows easy reconnection at runtime without causing vulnerability or rigid-related issues in other parts of the system.
- Subtype polymorphism not only makes the system more orthogonal and easier to scale, but it is also less likely to violate core paradigms such as the open/closed principle and the "interface-oriented programming" principle. It is a fundamental aspect of object-oriented programming that allows for flexibility and reusability of code.
Many people may doubt the correlation between inheritance and polymorphism in object-oriented design? Probably few, most of them may be due to ignorance or narrow thinking. But there is a small problem here that cannot be ignored. While it is simple to understand the logic of inheritance, things become more difficult when delving into the details of polymorphism. The term “polymorphism” is daunting in itself, with its academic definition full of different perspectives, which makes it even harder to understand what is actually behind it. Peripheral concepts such as parameter polymorphism and ad hoc polymorphism (usually implemented by method override/overload) do have significant applications in some programming languages, but in design, they can consume specific contracts (reads) When an abstract) extensible system, the last case should be abandoned without checking whether the implementer is of the expected type. In short, most of the time, any general reference to polymorphism in object-oriented programming is implicitly considered as a system-explicit capability used to define a set of contracts or interfaces that Or the interface is followed by different implementations. This "canonical" polymorphism is often referred to as a subtype polymorphism, because the implementor of an interface is considered to be a subtype of them, regardless of whether there is an actual hierarchy. As one might expect, understanding the nature of polymorphism is only half of the learning process; the other half is of course demonstrating how to design a polymorphic system so that it can adapt to a rather realistic situation without falling into just showing “some pretty teaching” code” (in many cases, it’s a cheap euphemism for toy code). In this article, I will show you how to take advantage of the benefits provided by polymorphism by developing an insertable cache component. The core functionality can later be extended to suit your needs by developing additional cache drivers.
Define the interface and implementation of components
The menu of options to choose from is by no means absent when building extensible cache components (if you are skeptical about this, just look at what's behind some popular frameworks). However, here, the components I provide have the clever ability to swap different cache drivers at runtime without modifying any client code. So, how can you do this without much effort during the development process? Well, the first step should be...yes, define an isolated cache contract that will be followed by different implementations later, thereby taking advantage of the benefits of polymorphism. At its most basic level, the above contract is as follows:
<?php namespace LibraryCache; interface CacheInterface { public function set($id, $data); public function get($id); public function delete($id); public function exists($id); }
CacheInterface
Interface is a skeleton contract that abstracts the behavior of common cache elements. With the interface, you can easily create some specific cache implementations that conform to their contracts. Since I want to keep it simple and easy to understand, the cache driver I set up will be just a lean duo: the first one uses the file system as the underlying backend for cache/get data, while the second one uses the APC extension behind the scenes. The following is a file-based cache implementation:
<?php namespace LibraryCache; class FileCache implements CacheInterface { const DEFAULT_CACHE_DIRECTORY = 'Cache/'; private $cacheDir; public function __construct($cacheDir = self::DEFAULT_CACHE_DIRECTORY) { $this->setCacheDir($cacheDir); } public function setCacheDir($cacheDir) { if (!is_dir($cacheDir)) { if (!mkdir($cacheDir, 0644)) { throw InvalidArgumentException('The cache directory is invalid.'); } } $this->cacheDir = $cacheDir; return $this; } public function set($id, $data) { if (!file_put_contents($this->cacheDir . $id, serialize($data), LOCK_EX)) { throw new RuntimeException("Unable to cache the data with ID '$id'."); } return $this; } public function get($id) { if (!$data = unserialize(@file_get_contents($this->cacheDir . $id, false))) { throw new RuntimeException("Unable to get the data with ID '$id'."); } return $data; } public function delete($id) { if (!@unlink($this->cacheDir . $id)) { throw new RuntimeException("Unable to delete the data with ID '$id'."); } return $this; } public function exists($id) { return file_exists($this->cacheDir . $id); } }The driving logic of the class should be easy to understand. The most relevant thing here so far is that it exposes a neat polymorphic behavior, as it faithfully achieves the early
. While this ability is sweet and charming, for its part, I wouldn't appreciate it considering the goal here is to create a cache component that can switch backends at runtime. Let us put in extra effort for teaching purposes and bring another streamlined implementation of FileCache
to life. The following implementation complies with the interface contract, but this time it is by using APC to extend the bundling method: CacheInterface
CacheInterface
<?php namespace LibraryCache; class ApcCache implements CacheInterface { public function set($id, $data, $lifeTime = 0) { if (!apc_store($id, $data, (int) $lifeTime)) { throw new RuntimeException("Unable to cache the data with ID '$id'."); } } public function get($id) { if (!$data = apc_fetch($id)) { throw new RuntimeException("Unable to get the data with ID '$id'."); } return $data; } public function delete($id) { if (!apc_delete($id)) { throw new RuntimeException("Unable to delete the data with ID '$id'."); } } public function exists($id) { return apc_exists($id); } }Class isn't the most dazzling APC wrapper you've ever seen in your career, it packs all the features you need to save, retrieve and delete data from memory. Let's applaud ourselves, because we have successfully implemented a lightweight cache module whose specific backend is not only easy to swap at runtime due to its polymorphism, but it is also extremely easy to add more backends in the future. Just write another implementation that complies with
. However, I should emphasize that the actual subtype polymorphism is achieved by implementing contracts defined through interface construction, which is a very common approach. However, nothing can stop you from being less orthodox and get the same result by switching an interface declared as a set of abstract methods (located in an abstract class). If you feel risky and want to go that bypass, you can reconstruct the contract and the corresponding implementation as follows: ApcCache
<?php namespace LibraryCache; interface CacheInterface { public function set($id, $data); public function get($id); public function delete($id); public function exists($id); }
<?php namespace LibraryCache; class FileCache implements CacheInterface { const DEFAULT_CACHE_DIRECTORY = 'Cache/'; private $cacheDir; public function __construct($cacheDir = self::DEFAULT_CACHE_DIRECTORY) { $this->setCacheDir($cacheDir); } public function setCacheDir($cacheDir) { if (!is_dir($cacheDir)) { if (!mkdir($cacheDir, 0644)) { throw InvalidArgumentException('The cache directory is invalid.'); } } $this->cacheDir = $cacheDir; return $this; } public function set($id, $data) { if (!file_put_contents($this->cacheDir . $id, serialize($data), LOCK_EX)) { throw new RuntimeException("Unable to cache the data with ID '$id'."); } return $this; } public function get($id) { if (!$data = unserialize(@file_get_contents($this->cacheDir . $id, false))) { throw new RuntimeException("Unable to get the data with ID '$id'."); } return $data; } public function delete($id) { if (!@unlink($this->cacheDir . $id)) { throw new RuntimeException("Unable to delete the data with ID '$id'."); } return $this; } public function exists($id) { return file_exists($this->cacheDir . $id); } }
<?php namespace LibraryCache; class ApcCache implements CacheInterface { public function set($id, $data, $lifeTime = 0) { if (!apc_store($id, $data, (int) $lifeTime)) { throw new RuntimeException("Unable to cache the data with ID '$id'."); } } public function get($id) { if (!$data = apc_fetch($id)) { throw new RuntimeException("Unable to get the data with ID '$id'."); } return $data; } public function delete($id) { if (!apc_delete($id)) { throw new RuntimeException("Unable to delete the data with ID '$id'."); } } public function exists($id) { return apc_exists($id); } }
From top to bottom, this is indeed a polymorphic approach, which is against the previously discussed method. Personally, this is just my personal statement, I prefer to use interface constructs to define contracts and use abstract classes only when encapsulating boilerplate implementations shared by several subtypes. You can choose the method that best suits your needs. At this point, I could put down the curtain, write some fancy ending comments, boast about our impressive coding skills, and brag about the flexibility of our cache components, but that would be a slouch on us. When there are client code that can consume multiple implementations, polymorphisms exhibit its most tempting aspects without checking whether these implementations are instances of some type, as long as they meet the expected contract. So let's reveal the aspect by connecting the cache component to a basic client view class, which will allow us to do some neat HTML caching effortlessly.
Put the cache driver into use
Caching HTML output via our example cache module is very simple and I will save any lengthy explanations at other times. The entire cache process can be simplified into a simple view class, similar to the following:
<?php namespace LibraryCache; abstract class AbstractCache { abstract public function set($id, $data); abstract public function get($id); abstract public function delete($id); abstract public function exists($id); }
<?php namespace LibraryCache; class FileCache extends AbstractCache { // the same implementation goes here }The most dazzling guy is the class constructor, which uses the early implementers of
and the CacheInterface
method. Since the last method's responsibility is to cache the view's template after it is pushed to the output buffer, it would be nice to take advantage of this capability and cache the entire HTML document. Assume that the default template of the view has the following structure: render()
<?php namespace LibraryCache; class ApcCache extends AbstractCache { // the same implementation goes here }Now, let's have a little fun and cache the document by providing an instance of the
class to the view: ApcCache
<?php namespace LibraryView; interface ViewInterface { public function setTemplate($template); public function __set($field, $value); public function __get($field); public function render(); }It's very good, right? But wait! I was so excited that I forgot to mention that the above code snippet would explode on any system that does not have the APC extension installed (naughty system administrator!). Does this mean that the carefully crafted cache module is no longer reusable? This is exactly where the file-based driver comes into play, which can be put into the client code without receiving any complaints:
<?php namespace LibraryView; use LibraryCacheCacheInterface; class View implements ViewInterface { const DEFAULT_TEMPLATE = 'default'; private $template; private $fields = array(); private $cache; public function __construct(CacheInterface $cache, $template = self::DEFAULT_TEMPLATE) { $this->cache = $cache; $this->setTemplate($template); } public function setTemplate($template) { $template = $template . '.php'; if (!is_file($template) || !is_readable($template)) { throw new InvalidArgumentException( "The template '$template' is invalid."); } $this->template = $template; return $this; } public function __set($name, $value) { $this->fields[$name] = $value; return $this; } public function __get($name) { if (!isset($this->fields[$name])) { throw new InvalidArgumentException( "Unable to get the field '$field'."); } return $this->fields[$name]; } public function render() { try { if (!$this->cache->exists($this->template)) { extract($this->fields); ob_start(); include $this->template; $this->cache->set($this->template, ob_get_clean()); } return $this->cache->get($this->template); } catch (RuntimeException $e) { throw new Exception($e->getMessage()); } } }The above single line of code explicitly states that the view will use the file system instead of shared memory to cache its output. This dynamic switching cache backend briefly illustrates why polymorphism is so important when designing highly decoupled modules. It allows us to easily reconnect things at runtime without spreading vulnerability/rigidity-related artifacts to other parts of our system.
Conclusion
Polymorphism is indeed one of those good things in life, and once you understand it, it makes you wonder how you can do without Its case continues for so long. Polymorphic systems are inherently more orthogonal, easier to scale, and less prone to violating core paradigms such as the open/closed principle and the wise “interface-oriented programming” principle. Although rather primitive, our cache module is a prominent example of these advantages. If you haven't refactored your application to take advantage of the benefits of polymorphism, you'd better hurry up because you missed the jackpot! Pictures from Fotolia
FAQs about subtype polymorphisms (FAQ)
What are the main differences between subtype polymorphisms and parameter polymorphisms?
Subtype polymorphism, also known as inclusion polymorphism, is a form of polymorphism in which a name represents instances of many different categories that are associated by a public superclass. Parameter polymorphism, on the other hand, allows a function or data type to process a value in the same way without relying on its type. Parameter polymorphism is a way to make a language more expressive while maintaining full static type safety.
How does subtype polymorphism work in Java?
In Java, subtype polymorphism is achieved by using inheritance and interfaces. Superclass reference variables can point to subclass objects. This allows Java to decide which method to call at runtime, which is called dynamic method scheduling. It is one of the powerful features of Java that enables it to support dynamic polymorphism.
Can you provide an example of subtype polymorphism?
Of course, let's consider a simple example in Java. Suppose we have a superclass called "Animal" and two subclasses "Dog" and "Cat". Both the "Dog" and "Cat" classes rewrite the "sound" method of the "Animal" class. Now, if we create an "Animal" reference pointing to a "Dog" or "Cat" object and call the "sound" method, Java will decide at runtime which class's "sound" method to call. This is an example of subtype polymorphism.
What is the significance of subtype polymorphism in programming?
Subtype polymorphism is a fundamental aspect of object-oriented programming. It allows for flexibility and reusability of the code. Using subtype polymorphism, you can design a common interface for a set of classes and then use this interface to interact with the objects of those classes in a unified way. This will result in cleaner, more intuitive and easier to maintain code.
What is the relationship between subtype polymorphism and Liskov replacement principle?
Liskov Substitution Principle (LSP) is a principle of object-oriented design that states that if a program is using a base class, it should be able to use any of its subclasses without the program knowing it. In other words, objects of superclasses should be able to be replaced by objects of subclasses without affecting the correctness of the program. Subtype polymorphism is a direct application of LSP.
Does all programming languages support subtype polymorphism?
No, not all programming languages support subtype polymorphism. It is mainly a feature of statically typed object-oriented programming languages such as Java, C, and C#. Dynamically typed languages like Python and JavaScript have different forms of polymorphism, called duck types.
What is the difference between static polymorphism and dynamic polymorphism?
Static polymorphism, also known as compile-time polymorphism, is achieved through method overloading. The decision about which method to call is made at compile time. On the other hand, dynamic polymorphism, also known as runtime polymorphism, is implemented through method rewriting. The decision about which method to call is made at runtime. Subtype polymorphism is a dynamic polymorphism.
Can you explain the concept of up-conversion in subtype polymorphism?
Upconversion is a process of treating derived class objects as base class objects. It is a key aspect of subtype polymorphism. When you upconvert a derived class object, you can call any method defined in the base class. However, if the method is rewritten in the derived class, the rewrite version will be called.
What is down-conversion in the context of subtype polymorphism?
Down conversion is the opposite of up conversion. It is the process of converting superclass objects into subclasses. When you need to access methods that only exist in subclasses, you can use down conversion. However, downconversion can be dangerous because it can cause a ClassCastException if the object being converted does not actually have the type you are converting to.
How does subtype polymorphism promote reusability of code?
Subtype polymorphism allows us to write more general and reusable code. By using superclass references to interact with subclass objects, we can write code for various objects as long as they all belong to subclasses of the same superclass. This means we can add new subclasses without changing the code that uses superclasses, which makes our code more flexible and easier to maintain.
The above is the detailed content of Subtype Polymorphism - Swapping Implementation at Runtime. For more information, please follow other related articles on the PHP Chinese website!

To protect the application from session-related XSS attacks, the following measures are required: 1. Set the HttpOnly and Secure flags to protect the session cookies. 2. Export codes for all user inputs. 3. Implement content security policy (CSP) to limit script sources. Through these policies, session-related XSS attacks can be effectively protected and user data can be ensured.

Methods to optimize PHP session performance include: 1. Delay session start, 2. Use database to store sessions, 3. Compress session data, 4. Manage session life cycle, and 5. Implement session sharing. These strategies can significantly improve the efficiency of applications in high concurrency environments.

Thesession.gc_maxlifetimesettinginPHPdeterminesthelifespanofsessiondata,setinseconds.1)It'sconfiguredinphp.iniorviaini_set().2)Abalanceisneededtoavoidperformanceissuesandunexpectedlogouts.3)PHP'sgarbagecollectionisprobabilistic,influencedbygc_probabi

In PHP, you can use the session_name() function to configure the session name. The specific steps are as follows: 1. Use the session_name() function to set the session name, such as session_name("my_session"). 2. After setting the session name, call session_start() to start the session. Configuring session names can avoid session data conflicts between multiple applications and enhance security, but pay attention to the uniqueness, security, length and setting timing of session names.

The session ID should be regenerated regularly at login, before sensitive operations, and every 30 minutes. 1. Regenerate the session ID when logging in to prevent session fixed attacks. 2. Regenerate before sensitive operations to improve safety. 3. Regular regeneration reduces long-term utilization risks, but the user experience needs to be weighed.

Setting session cookie parameters in PHP can be achieved through the session_set_cookie_params() function. 1) Use this function to set parameters, such as expiration time, path, domain name, security flag, etc.; 2) Call session_start() to make the parameters take effect; 3) Dynamically adjust parameters according to needs, such as user login status; 4) Pay attention to setting secure and httponly flags to improve security.

The main purpose of using sessions in PHP is to maintain the status of the user between different pages. 1) The session is started through the session_start() function, creating a unique session ID and storing it in the user cookie. 2) Session data is saved on the server, allowing data to be passed between different requests, such as login status and shopping cart content.

How to share a session between subdomains? Implemented by setting session cookies for common domain names. 1. Set the domain of the session cookie to .example.com on the server side. 2. Choose the appropriate session storage method, such as memory, database or distributed cache. 3. Pass the session ID through cookies, and the server retrieves and updates the session data based on the ID.


Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

mPDF
mPDF is a PHP library that can generate PDF files from UTF-8 encoded HTML. The original author, Ian Back, wrote mPDF to output PDF files "on the fly" from his website and handle different languages. It is slower than original scripts like HTML2FPDF and produces larger files when using Unicode fonts, but supports CSS styles etc. and has a lot of enhancements. Supports almost all languages, including RTL (Arabic and Hebrew) and CJK (Chinese, Japanese and Korean). Supports nested block-level elements (such as P, DIV),

SublimeText3 English version
Recommended: Win version, supports code prompts!

WebStorm Mac version
Useful JavaScript development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

SublimeText3 Linux new version
SublimeText3 Linux latest version