Complex classes like Post 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 Post, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 18 | abstract class Post implements PostInterface |
||
| 19 | { |
||
| 20 | protected $title; |
||
| 21 | |||
| 22 | protected $slug; |
||
| 23 | |||
| 24 | protected $abstract; |
||
| 25 | |||
| 26 | protected $content; |
||
| 27 | |||
| 28 | protected $rawContent; |
||
| 29 | |||
| 30 | protected $contentFormatter; |
||
| 31 | |||
| 32 | protected $tags; |
||
| 33 | |||
| 34 | protected $comments; |
||
| 35 | |||
| 36 | protected $enabled; |
||
| 37 | |||
| 38 | protected $publicationDateStart; |
||
| 39 | |||
| 40 | protected $createdAt; |
||
| 41 | |||
| 42 | protected $updatedAt; |
||
| 43 | |||
| 44 | protected $commentsEnabled = true; |
||
| 45 | |||
| 46 | protected $commentsCloseAt; |
||
| 47 | |||
| 48 | protected $commentsDefaultStatus; |
||
| 49 | |||
| 50 | protected $commentsCount = 0; |
||
| 51 | |||
| 52 | protected $author; |
||
| 53 | |||
| 54 | protected $image; |
||
| 55 | |||
| 56 | protected $collection; |
||
| 57 | |||
| 58 | /** |
||
| 59 | * {@inheritdoc} |
||
| 60 | */ |
||
| 61 | public function __construct() |
||
| 65 | |||
| 66 | /** |
||
| 67 | * {@inheritdoc} |
||
| 68 | */ |
||
| 69 | public function __toString() |
||
| 70 | { |
||
| 71 | return $this->getTitle() ?: 'n/a'; |
||
| 72 | } |
||
| 73 | |||
| 74 | /** |
||
| 75 | * {@inheritdoc} |
||
| 76 | */ |
||
| 77 | public function setTitle($title) |
||
| 78 | { |
||
| 79 | $this->title = $title; |
||
| 80 | |||
| 81 | $this->setSlug(Tag::slugify($title)); |
||
| 82 | } |
||
| 83 | |||
| 84 | /** |
||
| 85 | * {@inheritdoc} |
||
| 86 | */ |
||
| 87 | public function getTitle() |
||
| 88 | { |
||
| 89 | return $this->title; |
||
| 90 | } |
||
| 91 | |||
| 92 | /** |
||
| 93 | * {@inheritdoc} |
||
| 94 | */ |
||
| 95 | public function setAbstract($abstract) |
||
| 96 | { |
||
| 97 | $this->abstract = $abstract; |
||
| 98 | } |
||
| 99 | |||
| 100 | /** |
||
| 101 | * {@inheritdoc} |
||
| 102 | */ |
||
| 103 | public function getAbstract() |
||
| 104 | { |
||
| 105 | return $this->abstract; |
||
| 106 | } |
||
| 107 | |||
| 108 | /** |
||
| 109 | * {@inheritdoc} |
||
| 110 | */ |
||
| 111 | public function setContent($content) |
||
| 112 | { |
||
| 113 | $this->content = $content; |
||
| 114 | } |
||
| 115 | |||
| 116 | /** |
||
| 117 | * {@inheritdoc} |
||
| 118 | */ |
||
| 119 | public function getContent() |
||
| 120 | { |
||
| 121 | return $this->content; |
||
| 122 | } |
||
| 123 | |||
| 124 | /** |
||
| 125 | * {@inheritdoc} |
||
| 126 | */ |
||
| 127 | public function setEnabled($enabled) |
||
| 128 | { |
||
| 129 | $this->enabled = $enabled; |
||
| 130 | } |
||
| 131 | |||
| 132 | /** |
||
| 133 | * {@inheritdoc} |
||
| 134 | */ |
||
| 135 | public function getEnabled() |
||
| 136 | { |
||
| 137 | return $this->enabled; |
||
| 138 | } |
||
| 139 | |||
| 140 | /** |
||
| 141 | * {@inheritdoc} |
||
| 142 | */ |
||
| 143 | public function setSlug($slug) |
||
| 144 | { |
||
| 145 | $this->slug = $slug; |
||
| 146 | } |
||
| 147 | |||
| 148 | /** |
||
| 149 | * {@inheritdoc} |
||
| 150 | */ |
||
| 151 | public function getSlug() |
||
| 152 | { |
||
| 153 | return $this->slug; |
||
| 154 | } |
||
| 155 | |||
| 156 | /** |
||
| 157 | * {@inheritdoc} |
||
| 158 | */ |
||
| 159 | public function setPublicationDateStart(\DateTime $publicationDateStart = null) |
||
| 160 | { |
||
| 161 | $this->publicationDateStart = $publicationDateStart; |
||
| 162 | } |
||
| 163 | |||
| 164 | /** |
||
| 165 | * {@inheritdoc} |
||
| 166 | */ |
||
| 167 | public function getPublicationDateStart() |
||
| 168 | { |
||
| 169 | return $this->publicationDateStart; |
||
| 170 | } |
||
| 171 | |||
| 172 | /** |
||
| 173 | * {@inheritdoc} |
||
| 174 | */ |
||
| 175 | public function setCreatedAt(\DateTime $createdAt = null) |
||
| 176 | { |
||
| 177 | $this->createdAt = $createdAt; |
||
| 178 | } |
||
| 179 | |||
| 180 | /** |
||
| 181 | * {@inheritdoc} |
||
| 182 | */ |
||
| 183 | public function getCreatedAt() |
||
| 184 | { |
||
| 185 | return $this->createdAt; |
||
| 186 | } |
||
| 187 | |||
| 188 | /** |
||
| 189 | * {@inheritdoc} |
||
| 190 | */ |
||
| 191 | public function setUpdatedAt(\DateTime $updatedAt = null) |
||
| 192 | { |
||
| 193 | $this->updatedAt = $updatedAt; |
||
| 194 | } |
||
| 195 | |||
| 196 | /** |
||
| 197 | * {@inheritdoc} |
||
| 198 | */ |
||
| 199 | public function getUpdatedAt() |
||
| 200 | { |
||
| 201 | return $this->updatedAt; |
||
| 202 | } |
||
| 203 | |||
| 204 | /** |
||
| 205 | * {@inheritdoc} |
||
| 206 | */ |
||
| 207 | public function addComments(CommentInterface $comment) |
||
| 208 | { |
||
| 209 | $this->comments[] = $comment; |
||
| 210 | $comment->setPost($this); |
||
| 211 | } |
||
| 212 | |||
| 213 | /** |
||
| 214 | * {@inheritdoc} |
||
| 215 | */ |
||
| 216 | public function setComments($comments) |
||
| 217 | { |
||
| 218 | $this->comments = new \Doctrine\Common\Collections\ArrayCollection(); |
||
| 219 | |||
| 220 | foreach ($this->comments as $comment) { |
||
| 221 | $this->addComments($comment); |
||
| 222 | } |
||
| 223 | } |
||
| 224 | |||
| 225 | /** |
||
| 226 | * {@inheritdoc} |
||
| 227 | */ |
||
| 228 | public function getComments() |
||
| 229 | { |
||
| 230 | return $this->comments; |
||
|
|
|||
| 231 | } |
||
| 232 | |||
| 233 | /** |
||
| 234 | * {@inheritdoc} |
||
| 235 | */ |
||
| 236 | public function addTags(TagInterface $tags) |
||
| 237 | { |
||
| 238 | $this->tags[] = $tags; |
||
| 239 | } |
||
| 240 | |||
| 241 | /** |
||
| 242 | * {@inheritdoc} |
||
| 243 | */ |
||
| 244 | public function getTags() |
||
| 245 | { |
||
| 246 | return $this->tags; |
||
| 247 | } |
||
| 248 | |||
| 249 | /** |
||
| 250 | * {@inheritdoc} |
||
| 251 | */ |
||
| 252 | public function setTags($tags) |
||
| 253 | { |
||
| 254 | $this->tags = $tags; |
||
| 255 | } |
||
| 256 | |||
| 257 | public function prePersist() |
||
| 258 | { |
||
| 259 | if (!$this->getPublicationDateStart()) { |
||
| 260 | $this->setPublicationDateStart(new \DateTime()); |
||
| 261 | } |
||
| 262 | |||
| 263 | $this->setCreatedAt(new \DateTime()); |
||
| 264 | $this->setUpdatedAt(new \DateTime()); |
||
| 265 | } |
||
| 266 | |||
| 267 | public function preUpdate() |
||
| 268 | { |
||
| 269 | if (!$this->getPublicationDateStart()) { |
||
| 270 | $this->setPublicationDateStart(new \DateTime()); |
||
| 271 | } |
||
| 272 | |||
| 273 | $this->setUpdatedAt(new \DateTime()); |
||
| 274 | } |
||
| 275 | |||
| 276 | /** |
||
| 277 | * {@inheritdoc} |
||
| 278 | */ |
||
| 279 | public function getYear() |
||
| 280 | { |
||
| 281 | return $this->getPublicationDateStart()->format('Y'); |
||
| 282 | } |
||
| 283 | |||
| 284 | /** |
||
| 285 | * {@inheritdoc} |
||
| 286 | */ |
||
| 287 | public function getMonth() |
||
| 288 | { |
||
| 289 | return $this->getPublicationDateStart()->format('m'); |
||
| 290 | } |
||
| 291 | |||
| 292 | /** |
||
| 293 | * {@inheritdoc} |
||
| 294 | */ |
||
| 295 | public function getDay() |
||
| 296 | { |
||
| 297 | return $this->getPublicationDateStart()->format('d'); |
||
| 298 | } |
||
| 299 | |||
| 300 | /** |
||
| 301 | * {@inheritdoc} |
||
| 302 | */ |
||
| 303 | public function setCommentsEnabled($commentsEnabled) |
||
| 304 | { |
||
| 305 | $this->commentsEnabled = $commentsEnabled; |
||
| 306 | } |
||
| 307 | |||
| 308 | /** |
||
| 309 | * {@inheritdoc} |
||
| 310 | */ |
||
| 311 | public function getCommentsEnabled() |
||
| 312 | { |
||
| 313 | return $this->commentsEnabled; |
||
| 314 | } |
||
| 315 | |||
| 316 | /** |
||
| 317 | * {@inheritdoc} |
||
| 318 | */ |
||
| 319 | public function setCommentsCloseAt(\DateTime $commentsCloseAt = null) |
||
| 320 | { |
||
| 321 | $this->commentsCloseAt = $commentsCloseAt; |
||
| 322 | } |
||
| 323 | |||
| 324 | /** |
||
| 325 | * {@inheritdoc} |
||
| 326 | */ |
||
| 327 | public function getCommentsCloseAt() |
||
| 328 | { |
||
| 329 | return $this->commentsCloseAt; |
||
| 330 | } |
||
| 331 | |||
| 332 | /** |
||
| 333 | * {@inheritdoc} |
||
| 334 | */ |
||
| 335 | public function setCommentsDefaultStatus($commentsDefaultStatus) |
||
| 336 | { |
||
| 337 | $this->commentsDefaultStatus = $commentsDefaultStatus; |
||
| 338 | } |
||
| 339 | |||
| 340 | /** |
||
| 341 | * {@inheritdoc} |
||
| 342 | */ |
||
| 343 | public function getCommentsDefaultStatus() |
||
| 344 | { |
||
| 345 | return $this->commentsDefaultStatus; |
||
| 346 | } |
||
| 347 | |||
| 348 | /** |
||
| 349 | * {@inheritdoc} |
||
| 350 | */ |
||
| 351 | public function setCommentsCount($commentsCount) |
||
| 352 | { |
||
| 353 | $this->commentsCount = $commentsCount; |
||
| 354 | } |
||
| 355 | |||
| 356 | /** |
||
| 357 | * {@inheritdoc} |
||
| 358 | */ |
||
| 359 | public function getCommentsCount() |
||
| 360 | { |
||
| 361 | return $this->commentsCount; |
||
| 362 | } |
||
| 363 | |||
| 364 | /** |
||
| 365 | * {@inheritdoc} |
||
| 366 | */ |
||
| 367 | public function isCommentable() |
||
| 368 | { |
||
| 369 | if (!$this->getCommentsEnabled() || !$this->getEnabled()) { |
||
| 370 | return false; |
||
| 371 | } |
||
| 372 | |||
| 373 | if ($this->getCommentsCloseAt() instanceof \DateTime) { |
||
| 374 | return $this->getCommentsCloseAt()->diff(new \DateTime())->invert == 1 ? true : false; |
||
| 375 | } |
||
| 376 | |||
| 377 | return true; |
||
| 378 | } |
||
| 379 | |||
| 380 | /** |
||
| 381 | * {@inheritdoc} |
||
| 382 | */ |
||
| 383 | public function isPublic() |
||
| 391 | |||
| 392 | /** |
||
| 393 | * {@inheritdoc} |
||
| 394 | */ |
||
| 395 | public function setAuthor($author) |
||
| 399 | |||
| 400 | /** |
||
| 401 | * {@inheritdoc} |
||
| 402 | */ |
||
| 403 | public function getAuthor() |
||
| 407 | |||
| 408 | /** |
||
| 409 | * {@inheritdoc} |
||
| 410 | */ |
||
| 411 | public function setImage($image) |
||
| 412 | { |
||
| 413 | $this->image = $image; |
||
| 414 | } |
||
| 415 | |||
| 416 | /** |
||
| 417 | * {@inheritdoc} |
||
| 418 | */ |
||
| 419 | public function getImage() |
||
| 420 | { |
||
| 421 | return $this->image; |
||
| 422 | } |
||
| 423 | |||
| 424 | /** |
||
| 425 | * {@inheritdoc} |
||
| 426 | */ |
||
| 427 | public function setCollection(CollectionInterface $collection = null) |
||
| 431 | |||
| 432 | /** |
||
| 433 | * {@inheritdoc} |
||
| 434 | */ |
||
| 435 | public function getCollection() |
||
| 439 | |||
| 440 | /** |
||
| 441 | * @param $contentFormatter |
||
| 442 | */ |
||
| 443 | public function setContentFormatter($contentFormatter) |
||
| 447 | |||
| 448 | /** |
||
| 449 | * {@inheritdoc} |
||
| 450 | */ |
||
| 451 | public function getContentFormatter() |
||
| 455 | |||
| 456 | /** |
||
| 457 | * {@inheritdoc} |
||
| 458 | */ |
||
| 459 | public function setRawContent($rawContent) |
||
| 463 | |||
| 464 | /** |
||
| 465 | * {@inheritdoc} |
||
| 466 | */ |
||
| 467 | public function getRawContent() |
||
| 471 | } |
||
| 472 |
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.
Let’s take a look at an example:
Our function
my_functionexpects aPostobject, and outputs the author of the post. The base classPostreturns a simple string and outputting a simple string will work just fine. However, the child classBlogPostwhich is a sub-type ofPostinstead decided to return anobject, and is therefore violating the SOLID principles. If aBlogPostwere passed tomy_function, PHP would not complain, but ultimately fail when executing thestrtouppercall in its body.