{"id":5877,"date":"2022-04-01T16:15:42","date_gmt":"2022-04-01T14:15:42","guid":{"rendered":"https:\/\/www.luklagroup.com\/uncategorized\/how-to-optimize-existing-code-using-symfonys-design-pattern-strategy-and-compiler-pass\/"},"modified":"2025-02-06T17:11:09","modified_gmt":"2025-02-06T16:11:09","slug":"how-to-optimize-existing-code-using-symfonys-design-pattern-strategy-and-compiler-pass","status":"publish","type":"post","link":"https:\/\/www.luklagroup.com\/en\/change-adopt-en\/how-to-optimize-existing-code-using-symfonys-design-pattern-strategy-and-compiler-pass\/","title":{"rendered":"How to optimize existing code using Symfony&#8217;s Design Pattern Strategy and Compiler Pass?"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><strong><em>When you&#8217;re developing a project, you often see a collection of ifs one after the other, or a huge switch \/ case that executes code according to a state. This code is often unreadable and difficult to maintain. <\/em><\/strong><\/p>\n\n<p class=\"wp-block-paragraph\"><strong><em>But did you know that there&#8217;s a design pattern that avoids this? Symfony makes it easy to integrate. <\/em><\/strong><\/p>\n\n<h2 class=\"wp-block-heading\">Why do we use a Design Pattern Strategy?<\/h2>\n\n<p class=\"wp-block-paragraph\">During the development of an application in the real estate sector, we had to implement a function enabling users to<strong>obtain and display their home&#8217;s consumption figures<\/strong>.  <\/p>\n\n<p class=\"wp-block-paragraph\">Initially, it was only possible to manually enter meter readings (electricity, gas or other). Consumption was then calculated on the basis of these meter readings.   <\/p>\n\n<p class=\"wp-block-paragraph\">We then wanted to exploit the interoperability capabilities of energy suppliers, by <strong>automatically retrieving consumption data from Linky and Gazpar meters<\/strong>. But how could we do this while minimizing the impact on already functional code? <\/p>\n\n<p class=\"wp-block-paragraph\">The solution: <strong>Design Pattern Strategy<\/strong>.<\/p>\n\n<h2 class=\"wp-block-heading\">Definition of Design Pattern Strategy<\/h2>\n\n<p class=\"wp-block-paragraph\">The Design Pattern Strategy is a design pattern that enables algorithms to be selected and executed according to certain conditions, all in compliance with one of the <strong>SOLID<\/strong> principles: Single Responsibility.<\/p>\n\n<p class=\"wp-block-paragraph\">First, we&#8217;ll set up a service to run these different algorithms. We&#8217;ll call this service <em>ExternalConsumptionProvider<\/em>. Each algorithm will be represented by a service we&#8217;ll call provider. The purpose of a provider is to provide us with consumption data for a given category.   <\/p>\n\n<p class=\"wp-block-paragraph\">In our example, we will have <strong>2 providers<\/strong>:<\/p>\n\n<ul class=\"wp-block-list\"><li><em><code>EnedisDataConnectProvider<\/code>  : Who will send us the consumption data from our Linky meter.<\/em><\/li><li><em><code>GrdfGazparProvider<\/code>  : Who will send us the consumption data from our Gazpar meter.<\/em><\/li><\/ul>\n\n<p class=\"wp-block-paragraph\">Here&#8217;s a diagram to help you understand:<\/p>\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-full\"><img alt=\"\" decoding=\"async\" src=\"https:\/\/digiwin.fr\/wp-content\/uploads\/2022\/04\/MicrosoftTeams-image-14.png\" alt=\"\" class=\"wp-image-4073\"\/><\/figure><\/div>\n\n<p class=\"wp-block-paragraph\">This diagram shows our <code>ExternalConsommationProvider<\/code> service (the yellow block at the very top). This will contain a list of providers (the property visible via the little red square). <strong>Each provider will be an instance of<\/strong> <code>ConsommationProviderInterface<\/code> (the yellow block in the middle). <strong>This will require each provider to develop 2 methods<\/strong> (<code>support()<\/code> and <code>findByRange()<\/code>).   <\/p>\n\n<p class=\"wp-block-paragraph\">Finally, we find our 2 providers <code>EnedisDataConnectProvider<\/code> and <code>GrdfGazparProvider<\/code> (each implementing the interface in question, symbolized by the dotted arrow).<\/p>\n\n<p class=\"wp-block-paragraph\">The aim of our <code>ExternalConsommationProvider<\/code> service is, via its <code>findByRange<\/code> method, to <strong>return consumption data to us<\/strong>. Without even having developed our 2 providers, we can already develop this method: <\/p>\n\n<pre class=\"wp-block-code\"><code>public function findByRange(Logement $logement, DateTime $start, DateTime $end, User $user): array\n{\n    $consommations = &#91;];\n\n    foreach ($this-&gt;getProviders() as $provider) {\n        if ($provider-&gt;support($logement, $user)) {\n            $consommations = array_merge(\n                $consommations,\n                $provider-&gt;findByRange($logement, $start, $end, $user)\n            );\n        }\n    }\n\n    return $consommations;\n}\n<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\">Let&#8217;s take a look at this code: we loop over the providers of our service. If each provider is supported (in short, if the user wishes to obtain consumption data from that provider), we add the consumption data retrieved by the provider to our <code>$consommations<\/code> table. <\/p>\n\n<p class=\"wp-block-paragraph\">As far as our providers are concerned, all we need to do is develop the methods of our :<\/p>\n\n<pre class=\"wp-block-code\"><code>class EnedisDataConnectProvider implements ConsommationProviderInterface\n{\n    public function support(Logement $logement, User $user): bool\n    {\n        \/\/ On v\u00e9rifie si notre logement est bien connect\u00e9 au compteur Linky\n    }\n\n    public function findByRange(Logement $logement, DateTime $start, DateTime $end, User $user): array\n    {\n        \/\/ On retourne les consommations de notre compteur Linky gr\u00e2ce \u00e0 l'API Data Connect d'Enedis\n    }\n}\n<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\">Now that we&#8217;ve implemented our <code>ExternalConsommationProvider<\/code> service and our 2 providers, <strong>how do we integrate these providers into our service?<\/strong><\/p>\n\n<p class=\"wp-block-paragraph\">This is where <strong>Symfony&#8217;s Compiler Pass<\/strong> comes in.<\/p>\n\n<h2 class=\"wp-block-heading\">Compiler Pass definition<\/h2>\n\n<p class=\"wp-block-paragraph\">To add a provider to our service, we use the <strong> <code>addProvider()<\/code> method<\/strong>, which expects an instance of <code>ConsommationProviderInterface<\/code> as a parameter.<\/p>\n\n<p class=\"wp-block-paragraph\">With Symfony, we can tag our services to identify them more easily in our application.<\/p>\n\n<p class=\"wp-block-paragraph\">So we&#8217;re going to <strong>add a tag<\/strong> to all our services implementing the <code>ConsommationProviderInterface<\/code> interface. To do this, simply add the following lines to our <code>config\/services.yaml<\/code> file: <\/p>\n\n<pre class=\"wp-block-code\"><code>_instanceof:\n  App\\Service\\ConsommationProviderInterface:\n    tags: &#91; app.rt2012.external_consommation_provider ] \n<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\">So, our 2 providers will have the tags <code>app.rt2012.external_consommation_provider<\/code>.<\/p>\n\n<p class=\"wp-block-paragraph\">Now that we&#8217;ve <strong>identified our providers<\/strong>, we can create a CompilerPass. To do this, let&#8217;s create the <code>ExternalConsommationProviderPass<\/code> class like this: <\/p>\n\n<pre class=\"wp-block-code\"><code>class ExternalConsommationProviderPass implements CompilerPassInterface\n{\n    public function process(ContainerBuilder $container)\n    {\n        if (!$container-&gt;has(ExternalConsommationProvider::class)) {\n            return;\n        }\n\n        $definition = $container-&gt;findDefinition(ExternalConsommationProvider::class);\n\n        $taggedServices = $container-&gt;findTaggedServiceIds('app.rt2012.external_consommation_provider');\n\n        foreach ($taggedServices as $id =&gt; $tags) {\n            $definition-&gt;addMethodCall('addProvider', &#91;new Reference($id)]);\n        }\n    }\n}\n<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\">In our <code>process()<\/code> method, we check whether the <code>ExternalConsommationProvider<\/code> service exists. Next, we retrieve all services tagged with the name <code>app.rt2012.external_consommation_provider<\/code>, i.e. our 2 providers. Finally, we loop over these services to add them to our <code>ExternalConsommationProvider<\/code> service using the <code>addProvider()<\/code> method.  <\/p>\n\n<p class=\"wp-block-paragraph\">Finally, our application needs to take this CompilerPass into account. To do this, simply add it to the container, via the <code>build<\/code> method of the <code>App\\Kernel<\/code> class: <\/p>\n\n<pre class=\"wp-block-code\"><code>class Kernel extends BaseKernel\n{\n    public function build(ContainerBuilder $container)\n    {\n        $container-&gt;addCompilerPass(new ExternalConsommationProviderPass());\n    }\n}\n<\/code><\/pre>\n\n<h2 class=\"wp-block-heading\">The benefits of a Design Pattern Strategy<\/h2>\n\n<p class=\"wp-block-paragraph\">This is not the first and only time we&#8217;ve used this technique. We were able to exploit its advantages in the context of simple <strong>file imports<\/strong>. We&#8217;ve developed a service called <code>FileImporter<\/code> that can import two different file types, such as a <code>.xml<\/code> file and a <code>.yaml<\/code> file. This gives us two services: <code>XmlFileImporter<\/code> and <code>YamlFileImporter<\/code>. Each contains a <code>support<\/code> method for checking whether the file is compliant and a <code>execute<\/code> method for importing the file. Our main service will know which one to choose, depending on the file to be imported.     <\/p>\n\n<p class=\"wp-block-paragraph\"><strong>The main advantage of the Design Pattern Strategy is that it allows us to execute different algorithms according to a given state.  <\/strong><\/p>\n\n<p class=\"wp-block-paragraph\">Let&#8217;s imagine that, tomorrow, we also need to display the consumption of a solar self-consumption device. All we have to do is create a provider implementing the <code>ConsommationProviderInterface<\/code> interface.  <\/p>\n\n<p class=\"wp-block-paragraph\">Similarly, if we need to handle the import of another type of file (in <code>.json<\/code> format for example), we&#8217;ll simply need to create another service ( <code>JsonFileImporter<\/code> for example).  <\/p>\n\n<p class=\"wp-block-paragraph\">So we&#8217;ll be ready for future improvements \ud83d\ude09<\/p>\n","protected":false},"excerpt":{"rendered":"<p>When you&#8217;re developing a project, you often see a collection of ifs one after the other, or a huge switch \/ case that executes code according to a state. This code is often unreadable and difficult to maintain. But did you know that there&#8217;s a design pattern that avoids this? Symfony makes it easy to [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":5473,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_seopress_robots_primary_cat":"","_seopress_titles_title":"","_seopress_titles_desc":"","_seopress_robots_index":"","footnotes":""},"categories":[109,105],"tags":[137,139,141,142],"class_list":["post-5877","post","type-post","status-publish","format-standard","has-post-thumbnail","category-apps-data-en","category-change-adopt-en","tag-compile","tag-pattern-en","tag-provider-en","tag-symfony-en"],"_links":{"self":[{"href":"https:\/\/www.luklagroup.com\/en\/wp-json\/wp\/v2\/posts\/5877","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.luklagroup.com\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.luklagroup.com\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.luklagroup.com\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.luklagroup.com\/en\/wp-json\/wp\/v2\/comments?post=5877"}],"version-history":[{"count":2,"href":"https:\/\/www.luklagroup.com\/en\/wp-json\/wp\/v2\/posts\/5877\/revisions"}],"predecessor-version":[{"id":5881,"href":"https:\/\/www.luklagroup.com\/en\/wp-json\/wp\/v2\/posts\/5877\/revisions\/5881"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.luklagroup.com\/en\/wp-json\/wp\/v2\/media\/5473"}],"wp:attachment":[{"href":"https:\/\/www.luklagroup.com\/en\/wp-json\/wp\/v2\/media?parent=5877"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.luklagroup.com\/en\/wp-json\/wp\/v2\/categories?post=5877"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.luklagroup.com\/en\/wp-json\/wp\/v2\/tags?post=5877"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}