Complex classes like Kohana_Jam 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 Kohana_Jam, and based on these observations, apply Extract Interface, too.
1 | <?php defined('SYSPATH') OR die('No direct script access.'); |
||
16 | abstract class Kohana_Jam { |
||
17 | |||
18 | /** |
||
19 | * @var string The prefix to use for all model's class names |
||
20 | * This can be overridden to allow you to place |
||
21 | * models and collections in a different location. |
||
22 | */ |
||
23 | protected static $_model_prefix = 'Model_'; |
||
24 | |||
25 | /** |
||
26 | * @var string The prefix to use for all model's collection class names |
||
27 | * This can be overridden to allow you to place |
||
28 | * models and collections in a different location. |
||
29 | */ |
||
30 | protected static $_collection_prefix = 'Model_Collection_'; |
||
31 | |||
32 | /** |
||
33 | * @var string This prefix to use for all model's field classes |
||
34 | * This can be overridden to allow you to place |
||
35 | * field classes in a different location. |
||
36 | */ |
||
37 | protected static $_field_prefix = 'Jam_Field_'; |
||
38 | |||
39 | /** |
||
40 | * @var string This prefix to use for all behavior classes |
||
41 | * This can be overridden to allow you to place |
||
42 | * behavior classes in a different location. |
||
43 | */ |
||
44 | protected static $_behavior_prefix = 'Jam_Behavior_'; |
||
45 | |||
46 | /** |
||
47 | * @var string This prefix to use for all model's association classes |
||
48 | * This can be overridden to allow you to place |
||
49 | * association classes in a different location. |
||
50 | */ |
||
51 | protected static $_association_prefix = 'Jam_Association_'; |
||
52 | |||
53 | /** |
||
54 | * @var string This prefix to use for all model's form classes |
||
55 | * This can be overridden to allow you to place |
||
56 | * form classes in a different location. |
||
57 | */ |
||
58 | protected static $_form_prefix = 'Jam_Form_'; |
||
59 | |||
60 | /** |
||
61 | * @var string This prefix to use for all attribute's validator rule classes |
||
62 | * This can be overridden to allow you to place |
||
63 | * form classes in a different location. |
||
64 | */ |
||
65 | protected static $_validator_rule_prefix = 'Jam_Validator_Rule_'; |
||
66 | |||
67 | /** |
||
68 | * @var array Contains all of the meta classes related to models |
||
69 | */ |
||
70 | public static $_models = array(); |
||
71 | |||
72 | /** |
||
73 | * Hold model objects for templates |
||
74 | * @var array |
||
75 | */ |
||
76 | protected static $_build_templates = array(); |
||
77 | |||
78 | /** |
||
79 | * Make a new object of the given model, optionally setting some fields |
||
80 | * @param string $model_name |
||
81 | * @param array $attributes |
||
82 | * @return Jam_Model |
||
83 | */ |
||
84 | 414 | public static function build($model_name, array $attributes = NULL) |
|
104 | |||
105 | /** |
||
106 | * Create a new object of a given model, optionally setting some fields, and then save it to the database |
||
107 | * @param string $model |
||
108 | * @param array $attributes |
||
109 | * @return Jam_Model |
||
110 | */ |
||
111 | 4 | public static function create($model, array $attributes = array()) |
|
115 | |||
116 | /** |
||
117 | * Gets a particular set of metadata about a model. If the model |
||
118 | * isn't registered, it will attempt to register it. |
||
119 | * |
||
120 | * FALSE is returned on failure. |
||
121 | * |
||
122 | * @param string|Jam_Validated $model |
||
123 | * @return Jam_Meta |
||
124 | */ |
||
125 | 754 | public static function meta($model) |
|
139 | |||
140 | /** |
||
141 | * Factory for instantiating fields. |
||
142 | * |
||
143 | * @param string $type |
||
144 | * @param mixed $options |
||
145 | * @return Jam_Field |
||
146 | */ |
||
147 | 47 | public static function field($type, $options = NULL) |
|
153 | |||
154 | /** |
||
155 | * Factory for instantiating associations. |
||
156 | * |
||
157 | * @param string $type |
||
158 | * @param mixed $options |
||
159 | * @return Jam_Association |
||
160 | */ |
||
161 | 6 | public static function association($type, $options = NULL) |
|
167 | |||
168 | |||
169 | /** |
||
170 | * Factoring for instantiating behaviors. |
||
171 | * |
||
172 | * @param string $type |
||
173 | * @param mixed $options |
||
174 | * @return Jam_Behavior |
||
175 | */ |
||
176 | 5 | public static function behavior($type, $options = array()) |
|
177 | { |
||
178 | 5 | $behavior = Jam::$_behavior_prefix.Jam::capitalize_class_name($type); |
|
179 | |||
180 | 5 | return new $behavior($options); |
|
181 | } |
||
182 | |||
183 | /** |
||
184 | * Factoring for instantiating behaviors. |
||
185 | * |
||
186 | * @param string $type |
||
187 | * @param mixed $options |
||
188 | * @return Jam_Validator_Rule |
||
189 | */ |
||
190 | 208 | public static function validator_rule($type, $options = array()) |
|
196 | |||
197 | |||
198 | |||
199 | /** |
||
200 | * Automatically loads a model, if it exists, |
||
201 | * into the meta table. |
||
202 | * |
||
203 | * Models are not required to register |
||
204 | * themselves; it happens automatically. |
||
205 | * |
||
206 | * @param string $model |
||
207 | * @return boolean |
||
208 | */ |
||
209 | 108 | public static function register($model) |
|
245 | |||
246 | 503 | public static function capitalize_class_name($class_name) |
|
250 | |||
251 | /** |
||
252 | * Returns the class name of a model |
||
253 | * |
||
254 | * @param string|Jam_Validated $model |
||
255 | * @return string |
||
256 | */ |
||
257 | 479 | public static function class_name($model) |
|
268 | |||
269 | /** |
||
270 | * Returns the model name of a class |
||
271 | * |
||
272 | * @param string|Jam_Validated $model |
||
273 | * @return string |
||
274 | */ |
||
275 | 754 | public static function model_name($model) |
|
292 | |||
293 | /** |
||
294 | * Returns the prefix to use for all models and collections. |
||
295 | * |
||
296 | * @return string |
||
297 | */ |
||
298 | public static function model_prefix() |
||
302 | |||
303 | /** |
||
304 | * Returns the prefix to use for all models and collections. |
||
305 | * |
||
306 | * @return string |
||
307 | */ |
||
308 | 7 | public static function collection_prefix() |
|
312 | |||
313 | /** |
||
314 | * Returns the prefix to use for all fields. |
||
315 | * |
||
316 | * @return string |
||
317 | */ |
||
318 | public static function field_prefix() |
||
322 | |||
323 | /** |
||
324 | * Returns the prefix to use for forms. |
||
325 | * |
||
326 | * @return string |
||
327 | */ |
||
328 | public static function form_prefix() |
||
332 | |||
333 | |||
334 | /** |
||
335 | * Returns the prefix to use for all behaviors. |
||
336 | * |
||
337 | * @return string |
||
338 | */ |
||
339 | 7 | public static function behavior_prefix() |
|
343 | |||
344 | /** |
||
345 | * Clear the cache of the models. You should do this only when you dynamically change models |
||
346 | * @param string $name optionally clear only one model |
||
347 | */ |
||
348 | 5 | public static function clear_cache($name = NULL) |
|
349 | { |
||
350 | 5 | if ($name !== NULL) |
|
351 | { |
||
352 | unset(Jam::$_models[$name]); |
||
353 | } |
||
354 | else |
||
355 | { |
||
356 | 5 | Jam::$_models = array(); |
|
357 | } |
||
358 | 5 | } |
|
359 | |||
360 | /** |
||
361 | * Make an object of class Jam_Query_Builder_Delete |
||
362 | * @param string|Jam_Model $model |
||
363 | * @return Jam_Query_Builder_Delete |
||
364 | */ |
||
365 | 9 | public static function delete($model) |
|
369 | |||
370 | /** |
||
371 | * Make an object of class Jam_Query_Builder_Update |
||
372 | * @param string|Jam_Model $model |
||
373 | * @return Jam_Query_Builder_Update |
||
374 | */ |
||
375 | 25 | public static function update($model) |
|
379 | |||
380 | /** |
||
381 | * Make an object of class Jam_Query_Builder_Insert |
||
382 | * @param string|Jam_Model $model |
||
383 | * @return Jam_Query_Builder_Insert |
||
384 | */ |
||
385 | 17 | public static function insert($model, array $columns = array()) |
|
389 | |||
390 | /** |
||
391 | * Make an object of class Jam_Query_Builder_Select |
||
392 | * @param string|Jam_Model $model |
||
393 | * @return Jam_Query_Builder_Select |
||
394 | */ |
||
395 | 2 | public static function select($model) |
|
399 | |||
400 | /** |
||
401 | * Make an object of class Jam_Query_Builder_Collection |
||
402 | * @param string|Jam_Model $model |
||
403 | * @return Jam_Query_Builder_Collection |
||
404 | */ |
||
405 | 79 | public static function all($model) |
|
418 | |||
419 | 3 | protected static function find_or($method, $model, array $values) |
|
436 | |||
437 | /** |
||
438 | * Try to find a model with the given fields, if one cannot be found, |
||
439 | * build it and set the fields we've search with to it. |
||
440 | * @param string $model |
||
441 | * @param array $values |
||
442 | * @return Jam_Model |
||
443 | */ |
||
444 | public static function find_or_build($model, array $values) |
||
448 | |||
449 | /** |
||
450 | * Try to find a model with the given fields, if one cannot be found, |
||
451 | * create it and set the fields we've search with to it. Save the model to the database. |
||
452 | * @param string $model |
||
453 | * @param array $values |
||
454 | * @return Jam_Model |
||
455 | */ |
||
456 | 3 | public static function find_or_create($model, array $values) |
|
460 | |||
461 | /** |
||
462 | * Find a model with its unique key. Return NULL on failure. |
||
463 | * You can pass an array - then it tries to find all the models corresponding to the keys |
||
464 | * @param string $model |
||
465 | * @param int|string|int[] $key |
||
466 | * @return Jam_Model |
||
467 | */ |
||
468 | 21 | public static function find($model, $key) |
|
477 | |||
478 | /** |
||
479 | * Find a model with its unique key. Throw Jam_Exception_Notfound on failure |
||
480 | * You can pass an array of unique keys. If even one of them is not found, through Jam_Exception_Notfound |
||
481 | * @param string $model |
||
482 | * @param int|string|int[] $key |
||
483 | * @throws Jam_Exception_Invalidargument If id is array() or null |
||
484 | * @throws Jam_Exception_Notfound If no model was found |
||
485 | * @return Jam_Model |
||
486 | */ |
||
487 | 7 | public static function find_insist($model, $key) |
|
508 | |||
509 | /** |
||
510 | * Filter the $data array by removing everything that does not have a key in the permit array |
||
511 | * @param array $permit array of permitted keys |
||
512 | * @param array $data |
||
513 | * @return array |
||
514 | */ |
||
515 | 1 | public static function permit(array $permit = array(), array $data = array()) |
|
519 | |||
520 | 14 | public static function form($model, $class = NULL) |
|
541 | |||
542 | 56 | public static function build_template($model_name, array $values = NULL) |
|
555 | |||
556 | } // End Kohana_Jam |
||
557 |
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_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.