There are a lot of possible ways to use it. The most simple I think is pool
's usage for example. The main idea of virtual types is to use an abstract class or interface while you write your code and then overload it with the required implementation.
Imagine that you write a logger. You used network transport to send somewhere your logs and you write a lot of code using TransportInterface methods. To use TCP or UDP transport for logging now you just need to write an implementation for TransportInterface and create virtualType and then put it as a dependency for the logger.
For example:
interface TransportInterface {
public function send($data);
public function connect();
public function close();
....
}
class Logger
{
/** transport TransportInterface */
protected $transport;
...
public function __construct(
TransportInterface $transport
...
) {
$this->transport = $transport;
}
// some code that uses this transport's methods
}
class TransportUdp implement TransportInterface {...}
class TransportTcp implement TransportInterface {...}
And I want to send logs from one object (ObjectOne) by UDP (not so necessary) and from payment object(PaymentObject) by TCP (I need to know that all be written). So in both objects, I will put Logger class as a dependency but will write something like:
<virtualType name="LoggerUdp">
<arguments>
<argument name="transport" xsi:type="object">
TransportUdp
</argument>
</arguments>
</virtualType>
<virtualType name="LoggerTcp">
<arguments>
<argument name="transport" xsi:type="object">
TransportTcp
</argument>
</arguments>
</virtualType>
<type name="ObjectOne">
<arguments>
<argument name="logger" xsi:type="object">LoggerUdp</argument>
</arguments>
</type>
<type name="PaymentObject">
<arguments>
<argument name="logger" xsi:type="object">LoggerTcp</argument>
</arguments>
</type>
Take a look at /vendor/magento/module-catalog-search/etc/di.xml
at 135 and 145 lines for using if for search module in magento core.