Our web application allows users to upload files. We have two god objects in our web application:
User object (3000+ lines)
File object (3000+ lines)
Common usage at the moment is:
$file = File::getByID($_GET['file_id']);
$user = User::getByID($file->ownerID);
echo $file->title;
echo $user->name;
We are currently discussing adding the user object to the file object as another public variable. Because everywhere we use the file object, we need the associated user object.
So usage will be:
$file = File::getByID($_GET['file_id']);
echo $file->title;
echo $file->user->name;
We are trying to refactor these god objects, and adding the user object to the file object feels like we are making the problem worse. However I don't really have a good argument to disagree with the proposed change.
It would be great to get some help with the following questions:
Is adding the user object to the file object a bad idea?
If it's a bad idea, what is the main argument for not doing so?
How else can I go about refactoring these objects?
Notes
I know number of lines aren't a true measure, but our other classes are 50-150 lines and rarely used.
I've cut down the common usage to the important bits so apologies for lack of best practices.
I'm already trying to remove static methods and public variables from our code.
I would definitely add the User object as a property of the File class, since this is the actual representation of the data model behind it.
It will also give the code more clarity, and will produce more concise usage code of the File object.
This will also mean the File class does not have to do any checking for validity if an owner ID is set: if an object is passed, it can assume it is valid (or it should not have been able to be constructed).
class File
{
protected $owner;
/**
* #var User $owner mandatory
*/
public function __construct(User $owner)
{
$this->setOwner($owner);
}
public function setOwner(User $owner)
{
return $this->owner;
}
public function getOwner()
{
return $this->owner;
}
}
It is now clear that the File has a mandatory property owner, which is a User.
I would also recommend using accessors instead of public properties. This will be more future proof, for example it will also allow you to lazy load the user if you please to do so later on.
class File
{
protected $ownerId;
protected $owner;
public function getOwner()
{
if (!$this->owner && $this->ownerId) {
$this->owner = User::getById($this->ownerId);
}
return $this->owner;
}
}
This implies that your data layer fills the object accordingly, and would also require an adapted constructor, with the loss of automatic Type checking (only the constructor, still has type check in the setter):
/**
* #var $owner int|User the user object or it's ID
*/
public function __construct($owner)
{
if (is_object($owner)) $this->setOwner($owner);
else $this->ownerId = $owner;
}
As for the question of where your static User::getById() should go: you should separate the data operations (retrieval and persisting of objects) in a separate layer.
class UserStore
{
public function getById($id)
{
// code to retrieve data from where ever, probably database
// or maybe check if the user was already instantiated and registered somewhere...
$user = new User($data['name'], $data['email']/*, ....*/);
return $user;
}
}
For this last one, I would really not recommend building your own library (called an ORM), many great implementations exist. Take a look at Doctrine ORM.
Lastly I would say that reducing the size of a class should not be a goal in itself. you could however reduce it's complexity by extracting logic that is not inherent to the object itself, as is the case with the owner property.
Another example might be the File's path: maybe the path is stored relatively, and you have functions to convert that to an absolute path, or to get the extension or filename. This functionality can be extracted in a seperate FileMeta class or whatever you want to call is, maybe even a File class in a different namespace: FileSystem\File.
Related
For instance i have class say that runs MySQL query and i want to encapsulate this class.
class SqlQuery {
private $database_connection_obj;
public function __construct($dbh) {
$this->$database_connection_obj = $dbh;
}
public function runQuery($query, $params) {
//run query implementation...
}
}
Encapsulation and information hiding means that i want to close direct access to class methods so function runQuery() should not be public so i make it private and add method exacuteQuery() with sole purpose to pass data to private function runQuery().
What is the practical use of doing so, because at the end it works exact same as code above.
Should there be some sanitation of input data done in public method before its passed to private method or why write this extra code at all?
class SqlQuery {
private $database_connection_obj;
public function __construct($dbh) {
$this->$database_connection_obj = $dbh;
}
public function exacuteQuery($external_query, $external_params) {
$this->runQuery($external_query, $external_params);
}
private function runQuery($query, $params) {
//run query implementation...
}
}
The main idea is to make classes look like black boxes, meaning that an outsider (namely, another class or function holding an instance of the class) has no idea how it works internally. It's abstract to them.
The internal workings of the query are of no importance to the class holding the query and calling ->run(), it only cares about the query running, not necessarily how or where it runs.
Note that a query specifically is quite the low level abstraction, it's relatively close to making direct calls to standard library functions/objects. But given that low level abstraction, you can make higher level abstraction.
For example, a UserStore, that internally uses SqlQuery objects to get and set User objects into the database. A class using UserStore isn't aware that UserStore is using SqlQuery objects internally, it just knows that it's an object that can save and retrieve User objects.
This sort of "hiding away" and "encapsulating" actually gives you a lot of power, because now classes can use other classes without depending on specific implementation details. If tomorrow you'd like to change the way you store Users, you just make a change to the UserStore class, as long as it has the same public API, the rest of your application wouldn't even be aware that something changed.
Given details has no difference, but it depends upon work. I use it much times when i have to make a function which cannot be access directly but only by the method for its use. It optimize the code and deny access outside the code. Otherwise any one can alter the parameters which can be harmful.
Recently I have starting trying to do more OOP based programming as I know it's way better than procedural programming but I have a probably simple question. If I want to implement a "tasks" class where each tasks has a title, description, how far completed it is and who submitted it. I have the following code to create the class:
class Tasks
{
public $title;
public $description;
public $completionPercent;
public $contact;
public function __construct($title, $description, $completionPercent, $contact)
{
$this->title = $title;
$this->description = $description;
$this->completionPercent = $completionPercent;
$this->contact = $contact;
}
public function getTitle()
{
return $this->title;
}
public function getDescription()
{
return $this->description;
}
public function getCompletionPercent()
{
return $this->completionPercent;
}
public function getContact()
{
return $this->contact;
}
public function setTitle($title)
{
$this->title = $title;
}
public function setDescription($description)
{
$this->description = $description;
}
public function setCompletionPercent($completionPercent)
{
$this->completionPercent = $completionPercent;
}
public function setContact($contact)
{
$this->contact =$contact;
}
}
My question is if I create a tasks and then access it later as in close the browser and then come back to it a few days later when I have an update to it how do I store it and re-access it. I understand databases and use them very often in my current sites I am using to learn but I don't see how storing the information from the class in the database would be logical when I assume you could just skip the class and utilize the database. I read that you could serialize or store XML/json in a file and access the class that way but again I don't see why you would need the class if you could just read a file into an array and use it that way. I know OOP is important but I am still just trying to get it down to where I can start using classes regularly so that I can get familiar with them and progress as a programmer. I am sure there is plenty of reading out on the net about his but I just keep finding stuff on storing class variables in session variables which is not exactly what I want. Let me know if there is something I can clarify, I know when I first make the class I would do something like $task1 = new Task(...); but after closing my browser and logging back in I am not able to still access $task1 correct? I cannot just do $task1->getTitle(); so how do I re-initialize the class?
Classes and objects help you organise your code and logic into self contained units. That is all. And that is very important. It does not address the issue of storage or anything else.
Yes, you would still store the data of the object in the database, and then read again from the database and create an object instance again.
input → new Task(..) → store object data in database
read data from database → new Task(..) → display on website
Why is this better than working with arrays from the database directly? Because it allows you to organise your code better. Even if the object's lifetime is just a fraction of a second, within that fraction all the code that uses the object can be written much more elegantly and most of all type safe, because it is clearly defined what that object looks like. An array is an untyped grab bag of stuff which is hard to work with the more complex your application becomes. Especially refactoring code which uses only arrays is very very hard.
You are right, you need to store such information somewhere, and right place for that is db. So, question is: why you need to use and create class "Task" if you can directly save it in db. Because you want to build application based on "classes and objects", so not to use procedural programming, as you said. Therefore, you need to use objects and create classes for everything in order to have fully OO application.
So, your application should be based on classes/objects and this is what you and some other developers will work with. Now you just need to save this data from that object into the database. That means, you need introduce some additional layer for this. One very nice is Doctrine ORM. It does mapping and translate your object into db. Usually each class has its own table in db and each field in the class has its own column in db (this is only for classes which need to be saved in db)
The point is, that you can easily use objects in your application like any other objects without knowing how they should be saved in db (or somewhere else). Only what you need to do is:
$task->save();
and that's it. That task will be saved in db.
I see quite often (ie within the Slim framework) that single function accessor style (like in [1] below) is deprecated in favor to classical Java-ish 2 functions accessors (get/set) (like in [2] below).
Personally I rather prefer less lines of code (in [1]) and less typing (get/set) than being able to chain setter calls like in [3] (which I consider awful).
Am I missing something?
class Test {
protected $body;
// [1] single function accessor
public function body($body = null)
{
if (!is_null($body))
$this->body=$body;
return $this->body;
}
// [2] dual function accessors
public function getBody()
{
return $this->body;
}
// [2] dual function accessors
public function setBody($body)
{
$this->body=$body;
//[3] ok, you could return $this for chaining
}
}
Are Single Function Accessors A Bad Habit?
They are not a good idea. The reasons are simple:
They have multiple responsibilities (setting and getting data). Good functions have a single responsibility and do it well.
They mask intent. You can't look at the method call and understand what's going to happen.
What do you think the body() method does? Well, body is a noun. And having a method (which should be a verb) being a noun is confusing.
But what if the method was age(). Age is both a verb and a noun. So when you see $obj->age(), are you telling the object to give you its age? Or are you telling the object to age itself?
Whereas $obj->getAge() is crystal clear what you're trying to do. And $obj->getBody() is equally clear.
It adds complexity to methods.
Your overall complexity will stay the same (because the same logic exists), but it shifts to fewer methods. Which means that instead of having 2 dead simple methods, you have one more complicated method.
So yeah, I'd avoid it.
But let's step back a minute. Rather than asking the question if "Single Function Accessors" are a bad habit, let's ask the same question about accessors in general...
Are Property Accessors A Bad Habit?
My answer is: yes.
Depends on Role of Object:
What it depends on is the role of the object, and the role of the specific property. It's a case-by-case basis.
There are tons of different types of objects (domain objects, services, proxies, collections, etc). Some are stateful, and some are not.
If the object isn't stateful, it has no properties and hence we can ignore it.
For those objects that have state, why do they have that state?
If it's because they represent the state, then the state should be public (not saying the property should be public, but the state should be exposed to the outside world). So domain models which represent business entities should have public state.
class User {
public $name;
}
But if the role of the object isn't to represent the state, but to do something with it, then it shouldn't be exposed.
class UserMapper {
protected $pdo;
public function __construct(Pdo $connection) {
$this->pdo = $connection;
}
public function findUserById($id) {
...
}
In the mapper case, $pdo is incidental state. The mapper's job isn't to represent the data of the database connection, but it needs it to work.
The Bottom Line
The bottom line is never expose state that the object isn't directly representing.
Just because you have a property, doesn't mean you should expose it. In fact, often times you shouldn't expose it. This is known as Information Hiding.
Depends On Type Of State:
But what about types of objects that are stateful? Well, as it turns out there are two fundamental types of state:
Application State
Think Configuration and things like that. Basically state that's not defined at build time, but is known at runtime.
It's important to note that this state shouldn't change through the course of a request. And it also should be reasonably the same request-to-request (aside from deploys, etc).
User State
Think state and data that's derived or dependent upon user input. An obvious example would be data that was submitted from a form.
But a less obvious example would be if you use different renderers for different types of representations. So if a user requested a JSON response, the JSON rendering code being set in a property would be considered user state.
My assertion:
Property accessors for application state are bad. There's no reason that application state should change mid-run, therefore there's no reason that you should have to propagate the state.
Property accessors for user state may be OK. But there's more to it than that.
Depends On What The Accessor Does
In the case of your example:
public function setBody($body)
{
$this->body=$body;
}
You're essentially making $body a public property.
And there's nothing wrong with that. But why do you need a method? What's wrong with: public $body; in the first place?
Some people will say "properties are evil, they must never be public!". And that's hogwash, since that's exactly what you did with that accessor.
Now, if the accessor did some typing information (through type hints), or other validation logic (minimum length, etc), then that's a different story...
Or is it?
Let me give an example.
class Person {
public $name;
public $age;
}
vs
class StrictPerson {
protected $name;
protected $age;
public function setName($name) {
if (!is_string($name)) throw new BlahException();
if (strlen($name) < 10) throw new InvalidException();
$this->name = $name;
}
public function getName() {
return $this->name;
}
public function setAge($age) {
if (!is_int($age)) throw new ....
if ($age < 0 || $age > 150) throw new ...
$this->age = $age;
}
public function getAge() {
return $this->age;
}
}
Now, it should be clearly obvious that those properties are always valid. Right? Right? Right?
Well, no. What would happen if I created a child:
class LoosePerson extends StrictPerson {
public function setName($name) {
$this->name = $name;
}
public function setAge($age) {
$this->age = $age;
}
}
All of a sudden, all of our validation disappears. Now you could argue that was intended and it's the programmer's problem. Or you could simply change the properties to be private instead, to hold them always being valid. Right? Right? Right?
Well, no. What would happen if I did this:
$a = new StrictPerson;
$r = new ReflectionProperty($a, 'name');
$r->setAccessible(true);
$r->setValue($a, 'Bob');
I've just set an invalid value onto an object that's supposed to always validate.
The Bottom Line
Using accessors as validators only works if you always use them. And every tool that you use always uses them. Things like mysqli and PDO and Doctrine and PHPUnit which set the property directly rather than calling setters can cause massive problems.
Instead, you can use an external validator:
class PersonValidator {
public function validate(Person $person) {
if (!is_string($person->name)) {
throw new Blah...
}
if (strlen($person->name) < 10) {
throw new Blah...
}
if (!is_int($age)) throw new ....
if ($age < 0 || $age > 150) throw new ...
return true;
}
}
So, Are Property Accessors A Bad Habit?
I argue that yes, often times they are a bad habit.
Now, there are some cases where you should use them:
When you must represent that state in an interface
Properties are not specifiable in an interface. So if you must represent that the object exposes state in an interface, then you should use a getter and a setter.
I'd strongly urge you to consider why you are creating the interface. For simple data objects, it's often better to rely on the core object type (since polymorphism can't benefit due to there being no logic on the data object).
When you need to hide the internal representation for some reason.
An example would be a class that represents a unicode string. You may have an accessor method to get the raw value. But you'd want that to be a method so that you could convert the internal representation to a string of the proper character set.
That brings up an interesting point.
When you create an accessor, you should never create a property accessor. You should instead create a state accessor. The difference is that property accessors must always return a property or set it).
A state accessor on the other hand can "fake it" if it needs to. So our example above about the unicode string class, could internally represent the string as an array of code points. Then when you access the "string" state, it will convert the array into an actual string.
Objects Should Abstract
If you're going to use an accessor, it should be to abstract state. Not to represent it.
The Bottom Line / TLDR
Property accessors are a bad habit. If you're going to do that, make the properties public and use a validator object.
State accessors are not a bad habit. They are quite useful for maintaining useful abstractions.
Pass all your objects dependencies in the constructor and just have getters to access them, no need for setters most of the time.
Further to #Kleskowy and #marty answers, the best possible solution for doing this is like the following:
class Test {
protected $body;
public function body()
{
$args=func_get_args();
if (count($args)==1) //simple setter
$this->body=$args[0];
elseif (count($args)) //setter for an array
$this->body=$args;
else //getter
return $this->body;
}
}
The #marty's idea ends up in a nice way to create an array which is equivalent to PHP 5.4 [] syntax (except assoc arrays). But still compatible with PHP 5 to 5.3 ;-)
You can then write:
$a->body("a"); //a value
$a->body(null); //set to null is possible
$a->body("this","is","an","array"); //convenient way to initialize $body as an array
$a->body(); //get the value
Lately I have been trying to create my own PHP framework, just to learn from it (As we may look into some bigger and more robust framework for production). One design concept I currently have, is that most core classes mainly work on static functions within classes.
Now a few days ago, I've seen a few articles about "Static methods are death to testability". This concerned me as.. yeah.. my classes contain mostly static methods.. The main reason I was using static methods is that a lot of classes would never need more than one instance, and static methods are easy to approach in the global scope. Now I'm aware that static methods aren't actually the best way to do things, I'm looking for a better alternative.
Imagine the following code to get a config item:
$testcfg = Config::get("test"); // Gets config from "test"
echo $testcfg->foo; // Would output what "foo" contains ofcourse.
/*
* We cache the newly created instance of the "test" config,
* so if we need to use it again anywhere in the application,
* the Config::get() method simply returns that instance.
*/
This is an example of what I currently have. But according to some articles, this is bad.
Now, I could do this the way how, for example, CodeIgniter does this, using:
$testcfg = $this->config->get("test");
echo $testcfg->foo;
Personally, I find this harder to read. That's why I would prefer another way.
So in short, I guess I need a better approach to my classes. I would not want more than one instance to the config class, maintain readability and have easy access to the class. Any ideas?
Note that I'm looking for some best practice or something including a code sample, not some random ideas. Also, if I'm bound to a $this->class->method style pattern, then would I implement this efficiently?
In response to Sébastien Renauld's comments: here's an article on Dependency Injection (DI) and Inversion of Control (IoC) with some examples, and a few extra words on the Hollywood principle (quite important when working on a framework).
Saying your classes won't ever need more than a single instance doesn't mean that statics are a must. Far from it, actually. If you browse this site, and read through PHP questions that deal with the singleton "pattern", you'll soon find out why singletons are a bit of a no-no.
I won't go into the details, but testing and singletons don't mix. Dependency injection is definitely worth a closer look. I'll leave it at that for now.
To answer your question:
Your exaple (Config::get('test')) implies you have a static property in the Config class somewhere. Now if you've done this, as you say, to facilitate access to given data, imagine what a nightmare it would be to debug your code, if that value were to change somewhere... It's a static, so change it once, and it's changed everywhere. Finding out where it was changed might be harder than you anticipated. Even so, that's nothing compared to the issues someone who uses your code will have in the same situation.
And yet, the real problems will only start when that person using your code wants to test whatever it is he/she made: If you want to have access to an instance in a given object, that has been instantiated in some class, there are plenty of ways to do so (especially in a framework):
class Application
{//base class of your framework
private $defaulDB = null;
public $env = null;
public function __construct($env = 'test')
{
$this->env = $env;
}
private function connectDB(PDO $connection = null)
{
if ($connection === null)
{
$connection = new PDO();//you know the deal...
}
$this->defaultDB = $connection;
}
public function getDB(PDO $conn = null)
{//get connection
if ($this->defaultDB === null)
{
$this->connectDB($conn);
}
return $this->defaultDB;
}
public function registerController(MyConstroller $controller)
{//<== magic!
$controller->registerApplication($this);
return $this;
}
}
As you can see, the Application class has a method that passes the Application instance to your controller, or whatever part of your framework you want to grant access to scope of the Application class.
Note that I've declared the defaultDB property as a private property, so I'm using a getter. I can, if I wanted to, pass a connection to that getter. There's a lot more you can do with that connection, of course, but I can't be bothered writing a full framework to show you everything you can do here :).
Basically, all your controllers will extend the MyController class, which could be an abstract class that looks like this:
abstract class MyController
{
private $app = null;
protected $db = null;
public function __construct(Application $app = null)
{
if ($app !== null)
{
return $this->registerApplication($app);
}
}
public function registerApplication(Application $app)
{
$this->app = $app;
return $this;
}
public function getApplication()
{
return $this->app;
}
}
So in your code, you can easily do something along the lines of:
$controller = new MyController($this);//assuming the instance is created in the Application class
$controller = new MyController();
$controller->registerApplication($appInstance);
In both cases, you can get that single DB instance like so:
$controller->getApplication()->getDB();
You can test your framework with easily by passing a different DB connection to the getDB method, if the defaultDB property hasn't been set in this case. With some extra work you can register multiple DB connections at the same time and access those at will, too:
$controller->getApplication->getDB(new PDO());//pass test connection here...
This is, by no means, the full explanation, but I wanted to get this answer in quite quickly before you end up with a huge static (and thus useless) codebase.
In response to comments from OP:
On how I'd tackle the Config class. Honestly, I'd pretty much do the same thing as I'd do with the defaultDB property as shown above. But I'd probably allow for more targeted control on what class gets access to what part of the config:
class Application
{
private $config = null;
public function __construct($env = 'test', $config = null)
{//get default config path or use path passed as argument
$this->config = new Config(parse_ini_file($config));
}
public function registerController(MyController $controller)
{
$controller->setApplication($this);
}
public function registerDB(MyDB $wrapper, $connect = true)
{//assume MyDB is a wrapper class, that gets the connection data from the config
$wrapper->setConfig(new Config($this->config->getSection('DB')));
$this->defaultDB = $wrapper;
return $this;
}
}
class MyController
{
private $app = null;
public function getApplication()
{
return $this->app;
}
public function setApplication(Application $app)
{
$this->app = $app;
return $this;
}
//Optional:
public function getConfig()
{
return $this->app->getConfig();
}
public function getDB()
{
return $this->app->getDB();
}
}
Those last two methods aren't really required, you could just as well write something like:
$controller->getApplication()->getConfig();
Again, this snippet is all a bit messy and incomplete, but it does go to show you that you can "expose" certain properties of one class, by passing a reference to that class to another. Even if the properties are private, you can use getters to access them all the same. You can also use various register-methods to control what it is the registered object is allowed to see, as I've done with the DB-wrapper in my snippet. A DB class shouldn't deal with viewscripts and namespaces, or autoloaders. That's why I'm only registering the DB section of the config.
Basically, a lot of your main components will end up sharing a number of methods. In other words, they'll end up implementing a given interface. For each main component (assuming the classic MVC pattern), you'll have one abstract base-class, and an inheritance chain of 1 or 2 levels of child classes: Abstract Controller > DefaultController > ProjectSpecificController.
At the same time, all of these classes will probably expect another instance to be passed to them when constructed. Just look at the index.php of any ZendFW project:
$application = new Zend_Application(APPLICATION_ENV);
$application->bootstrap()->run();
That's all you can see, but inside the application, all other classes are being instantiated. That's why you can access neigh on everything from anywhere: all classes have been instantiated inside another class along these lines:
public function initController(Request $request)
{
$this->currentController = $request->getController();
$this->currentController = new $this->currentController($this);
return $this->currentController->init($request)
->{$request->getAction().'Action'}();
}
By passing $this to the constructor of a controller class, that class can use various getters and setters to get to whatever it needs... Look at the examples above, it could use getDB, or getConfig and use that data if that's what it needs.
That's how most frameworks I've tinkered or worked with function: The application is kicks into action and determines what needs to be done. That's the Hollywood-principle, or Inversion of Control: the Application is started, and the application determines what classes it needs when. In the link I provided I believe this is compared to a store creating its own customers: the store is built, and decides what it wants to sell. In order to sell it, it will create the clients it wants, and provide them with the means they need to purchase the goods...
And, before I forget: Yes, all this can be done without a single static variable, let alone function, coming into play. I've built my own framework, and I've never felt there was no other way than to "go static". I did use the Factory pattern at first, but ditched it pretty quickly.
IMHO, a good framework is modular: you should be able to use bits of it (like Symfony's components), without issues. Using the Factory pattern makes you assume too much. You assume class X will be available, which isn't a given.
Registering those classes that are available makes for far more portable components. Consider this:
class AssumeFactory
{
private $db = null;
public function getDB(PDO $db = null)
{
if ($db === null)
{
$config = Factory::getConfig();//assumes Config class
$db = new PDO($config->getDBString());
}
$this->db = $db;
return $this->db;
}
}
As opposed to:
class RegisteredApplication
{//assume this is registered to current Application
public function getDB(PDO $fallback = null, $setToApplication = false)
{
if ($this->getApplication()->getDB() === null)
{//defensive
if ($setToApplication === true && $fallback !== null)
{
$this->getApplication()->setDB($fallback);
return $fallback;//this is current connection
}
if ($fallback === null && $this->getApplication()->getConfig() !== null)
{//if DB is not set #app, check config:
$fallback = $this->getApplication()->getConfig()->getSection('DB');
$fallback = new PDO($fallback->connString, $fallback->user, $fallback->pass);
return $fallback;
}
throw new RuntimeException('No DB connection set #app, no fallback');
}
if ($setToApplication === true && $fallback !== null)
{
$this->getApplication()->setDB($fallback);
}
return $this->getApplication()->getDB();
}
}
Though the latter version is slightly more work to write, it's quite clear which of the two is the better bet. The first version just assumes too much, and doesn't allow for safety-nets. It's also quite dictatorial: suppose I've written a test, and I need the results to go to another DB. I therefore need to change the DB connection, for the entire application (user input, errors, stats... they're all likely to be stored in a DB).
For those two reasons alone, the second snippet is the better candidate: I can pass another DB connection, that overwrites the application default, or, if I don't want to do that, I can either use the default connection, or attempt to create the default connection. Store the connection I just made, or not... the choice is entirely mine. If nothing works, I just get a RuntimeException thrown at me, but that's not the point.
Magic methods would help you: see the examples about __get() and __set()
You should also take a look at namespaces: it may help you to get rid of some classes with static methods only.
I've written a few classes and have come to a fork in the road about what I should do. My base question is, how do I avoid duplicating my code across classes with very similar functionality? Traits are not an option for me right now, and I don't think they would help too much here anyway.
I have the following classes implemented.
//either a directory or a file on the file system
class FileSystem_Object{
//the size of the class in bytes
public function getSize(){}
//same as phps native realpath
public function getRealPath(){}
}
//a zip file on the file system, e.g. files that end in .zip extension.
class FileSystem_Object_Zip extends FileSystem_Object{
//returns size of all files if they were to be uncompressed, in bytes
public function getUncompressedSize(){}
}
//a singleton file that keeps track of only one object copy of a file
class FileSystem_Manager{}
These classes sortof provide me with some SPLFileObject type functionality. I can do the following kind of stuff
$object =
FileSystem_Manager::getInstance()
->getFileSystemObjecT( '/some/path/to/file/or/directory/' );
Every time I call the getFileSystemObject method it will either return a new class object or return an object that was assigned to that path already, thus avoiding me creating multiple objects that point to the same path on the filesystem. ( maybe not the best idea but that's what I went with. )
Here's where we get to the issue a bit.
I have another set of classes that I use to 'lock' objects. Right now the only objects that I'm locking are filesystem_objects, regardless of whether they're directories or files. This works simply enough by creating a lock file for the file based on the process id of the php process trying to lock it.
inteface Lockable_Object{
public functon getLockableIdentifier();
}
class Lockable_FileSystemObject implements Lockable_Object{
/**
* I return a false here if the file doesn't exist
* so any other processes trying to lock this file will
* know they can no longer lock it because it has moved
* or been renamed for some reason.
*/
public functon getLockableIdentifier(){
if( file_exists( $this->_fullFilePath ) ){
return $this->getRealPath();
}
return false;
}
}
The problem I face now is that I'd like to create a Zip file object that can be locked as well, and I'd like to be able to lock pretty much any file/directory but I DON'T want to have to duplicate code. Which of the following should I do
//Option 1
class Lockable_Object_Zip extends FileSystem_Object_Zip
implements Lockable_Object{
//In here I would have to duplicate the getLockableIdentifier method and do that
//for every file type i decide to make in the future
}
//Option 2
class Lockable_Object_Zip extends Lockable_FileSystemObject
//In here I would have to duplicate all the zip functionality coded into
//FileSystem_Object_Zip
}
//Option 3
class FileSystem_Object implements Lockable_Object{
//build in the 'lockablity' into the base class
}
Right now I'm leaning towards option 3 but the only reason I would not like to do that is because then I would HAVE to have the 'Locker' part of my library whenever I want to use the file system stuff; it would be more tightly coupled.
I'm sure you'll have comments about the design and some will say "SplFileObject does all/most of this". I've included methods in here for examples and not all the methods I implemented are here so this isn't the only functionality I've written. All these comments and more are welcome, however, because they might land me on a design that will avoid this whole issue.
Thank you
In case the type of the locked classes doesn't matter, you could go with a Decorator pattern, e.g.
class Lockable
{
protected $lockable;
public function __construct($lockable)
{
$this->lockable = $lockable;
}
public function lock()
{
// .. your code to lock $this->lockable
}
public function __call($method, $args)
{
return call_user_func_array(array($this->lockable, $method), $args);
}
}
This way you are not duplicating logic. The drawback is that you cannot use the decorated instance in methods requiring the decorated type (unless you add appropriate interfaces and delegate all calls).
The Strategy pattern would be another option:
class LockingStrategy
{
public function lock($fileSystemObject)
{
// your code to lock $fileSystemObject
}
}
class ZipFile
…
public function __construct(LockingStrategy $strategy)
{
$this->lockingStrategy = $strategy;
}
public function lock()
{
$this->lockingStrategy->lock($this);
}
}
I think you should look into the Strategy pattern. Consider using composition of a Lockability strategy rather than trying to inherit it.