Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like AbstractWebTemplate often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use AbstractWebTemplate, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
39 | abstract class AbstractWebTemplate extends AbstractTemplate |
||
40 | { |
||
41 | use ContextualTemplateTrait; |
||
42 | use DebugAwareTrait; |
||
43 | use DocumentTrait; |
||
44 | use LocaleAwareTrait; |
||
45 | use ModelFactoryTrait; |
||
46 | use TranslatorAwareTrait; |
||
47 | |||
48 | /** |
||
49 | * The application's configuration container. |
||
50 | * |
||
51 | * @var AppConfig |
||
52 | */ |
||
53 | protected $appConfig; |
||
54 | |||
55 | /** |
||
56 | * The base URI. |
||
57 | * |
||
58 | * @var UriInterface|null |
||
59 | */ |
||
60 | protected $baseUrl; |
||
61 | |||
62 | /** |
||
63 | * The default image for social media sharing. |
||
64 | * |
||
65 | * @var string |
||
66 | */ |
||
67 | const DEFAULT_SOCIAL_MEDIA_IMAGE = ''; |
||
68 | |||
69 | /** |
||
70 | * Additional SEO metadata. |
||
71 | * |
||
72 | * @var array |
||
73 | */ |
||
74 | private $seoMetadata = []; |
||
75 | |||
76 | /** |
||
77 | * Inject dependencies from a DI Container. |
||
78 | * |
||
79 | * @param Container $container A dependencies container instance. |
||
80 | * @return void |
||
81 | */ |
||
82 | protected function setDependencies(Container $container) |
||
97 | |||
98 | /** |
||
99 | * Retrieve the title of the page (the context). |
||
100 | * |
||
101 | * @return string|null |
||
102 | */ |
||
103 | public function title() |
||
113 | |||
114 | /** |
||
115 | * Retrieve the current URI of the context. |
||
116 | * |
||
117 | * @return \Psr\Http\Message\UriInterface|null |
||
118 | */ |
||
119 | View Code Duplication | public function currentUrl() |
|
129 | |||
130 | /** |
||
131 | * Retrieve the current locale. |
||
132 | * |
||
133 | * @return string|null |
||
134 | */ |
||
135 | public function currentLocale() |
||
150 | |||
151 | /** |
||
152 | * Retrieve the current locale's language code. |
||
153 | * |
||
154 | * @return string |
||
155 | */ |
||
156 | public function currentLanguage() |
||
160 | |||
161 | |||
162 | |||
163 | // Metadata |
||
164 | // ========================================================================= |
||
165 | |||
166 | /** |
||
167 | * Retrieve the canonical URI of the object. |
||
168 | * |
||
169 | * @return \Psr\Http\Message\UriInterface|string|null |
||
170 | */ |
||
171 | public function canonicalUrl() |
||
175 | |||
176 | /** |
||
177 | * Parse the document title parts. |
||
178 | * |
||
179 | * @return string[] |
||
180 | */ |
||
181 | protected function documentTitleParts() |
||
188 | |||
189 | /** |
||
190 | * Retrieve the name or title of the object. |
||
191 | * |
||
192 | * @return string|null |
||
193 | */ |
||
194 | public function metaTitle() |
||
209 | |||
210 | /** |
||
211 | * Hook called as a fallback if no meta title is set on the object. |
||
212 | * |
||
213 | * This method should be extended by child controllers. |
||
214 | * |
||
215 | * @return string|null |
||
216 | */ |
||
217 | protected function fallbackMetaTitle() |
||
221 | |||
222 | /** |
||
223 | * Retrieve the description of the object. |
||
224 | * |
||
225 | * @return string|null |
||
226 | */ |
||
227 | View Code Duplication | public function metaDescription() |
|
242 | |||
243 | /** |
||
244 | * Hook called as a fallback if no meta description is set on the object. |
||
245 | * |
||
246 | * This method should be extended by child controllers. |
||
247 | * |
||
248 | * @return string|null |
||
249 | */ |
||
250 | protected function fallbackMetaDescription() |
||
254 | |||
255 | /** |
||
256 | * Retrieve the URL to the image representing the object. |
||
257 | * |
||
258 | * @return string|null |
||
259 | */ |
||
260 | public function metaImage() |
||
275 | |||
276 | /** |
||
277 | * Hook called as a fallback if no meta image is set on the object. |
||
278 | * |
||
279 | * This method should be extended by child controllers. |
||
280 | * |
||
281 | * @return string|null |
||
282 | */ |
||
283 | protected function fallbackMetaImage() |
||
287 | |||
288 | /** |
||
289 | * Retrieve the URL to the image representing the object. |
||
290 | * |
||
291 | * @param string|null $img A path to an image. |
||
292 | * @return string|null |
||
293 | */ |
||
294 | protected function resolveMetaImage($img = null) |
||
307 | |||
308 | /** |
||
309 | * Retrieve the object's {@link https://developers.facebook.com/docs/reference/opengraph/ OpenGraph type}, |
||
310 | * for the "og:type" meta-property. |
||
311 | * |
||
312 | * @return string|null |
||
313 | */ |
||
314 | View Code Duplication | public function opengraphType() |
|
330 | |||
331 | /** |
||
332 | * Retrieve the URL to the object's social image for the "og:image" meta-property. |
||
333 | * |
||
334 | * This method can fallback onto {@see MetadataInterface::defaultMetaImage()} |
||
335 | * for a common image between web annotation schemas. |
||
336 | * |
||
337 | * @return string|null |
||
338 | */ |
||
339 | public function opengraphImage() |
||
359 | |||
360 | /** |
||
361 | * Hook called as a fallback if no social image is set on the object. |
||
362 | * |
||
363 | * This method should be extended by child controllers. |
||
364 | * |
||
365 | * @return string|null |
||
366 | */ |
||
367 | protected function fallbackOpengraphImage() |
||
371 | |||
372 | /** |
||
373 | * Set additional SEO metadata. |
||
374 | * |
||
375 | * @return iterable |
||
376 | */ |
||
377 | public function seoMetadata() |
||
381 | |||
382 | /** |
||
383 | * Determine if we have additional SEO metadata. |
||
384 | * |
||
385 | * @return boolean |
||
386 | */ |
||
387 | public function hasSeoMetadata() |
||
395 | |||
396 | /** |
||
397 | * Set additional SEO metadata. |
||
398 | * |
||
399 | * @param array $metadata Map of metadata keys and values. |
||
400 | * @return self |
||
401 | */ |
||
402 | protected function setSeoMetadata(array $metadata) |
||
421 | |||
422 | |||
423 | |||
424 | // App |
||
425 | // ========================================================================= |
||
426 | |||
427 | /** |
||
428 | * Set the application's configset. |
||
429 | * |
||
430 | * @param AppConfig $appConfig A Charcoal application configset. |
||
431 | * @return self |
||
432 | */ |
||
433 | protected function setAppConfig(AppConfig $appConfig) |
||
439 | |||
440 | /** |
||
441 | * Retrieve the application's configset or a specific setting. |
||
442 | * |
||
443 | * @param string|null $key Optional data key to retrieve from the configset. |
||
444 | * @param mixed|null $default The default value to return if data key does not exist. |
||
445 | * @return mixed|AppConfig|SettingsInterface |
||
446 | */ |
||
447 | public function appConfig($key = null, $default = null) |
||
463 | |||
464 | /** |
||
465 | * Set the base URI of the project. |
||
466 | * |
||
467 | * @see \Charcoal\App\ServiceProvider\AppServiceProvider `$container['base-url']` |
||
468 | * @param UriInterface $uri The base URI. |
||
469 | * @return self |
||
470 | */ |
||
471 | protected function setBaseUrl(UriInterface $uri) |
||
477 | |||
478 | /** |
||
479 | * Retrieve the base URI of the project. |
||
480 | * |
||
481 | * @throws RuntimeException If the base URI is missing. |
||
482 | * @return UriInterface|null |
||
483 | */ |
||
484 | public function baseUrl() |
||
495 | |||
496 | /** |
||
497 | * Prepend the base URI to the given path. |
||
498 | * |
||
499 | * @param string $uri A URI path to wrap. |
||
500 | * @return UriInterface |
||
501 | */ |
||
502 | public function createAbsoluteUrl($uri) |
||
521 | } |
||
522 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.