Skip to main content

This site requires you to update your browser. Your browsing experience maybe affected by not having the most up to date version.

General Questions /

General questions about getting started with SilverStripe that don't fit in any of the categories above.

Moderators: martimiz, Sean, biapar, Willr, Ingo, swaiba, simon_w

Instanting Objects


Reply


6 Posts   430 Views

Avatar
Bonner

Community Member, 21 Posts

3 June 2014 at 4:42am

I have a query regarding the various ways you can create a new instance of an object. I was wondering why there are 4 ways we can create an object in SilverStripe:

1. Using the singleton function
2. Using dependency injection
3. Using a Factory
4. Autoloading

Is there a specific reason there are so many options?

I am aware that autoloading allows multiple instances of the same class, and dependency injection gives you the option to reuse an object or create a new object, but then we have a Factory and a singleton function.

In my view, it is all a bit of a mess at the moment.

Avatar
martimiz

Forum Moderator, 1132 Posts

4 June 2014 at 12:51am

If iyou need to instantiate an object 'by hand', in version 2.x, you'd use

$myobj = new MyObject(...);

Of course this still works in version 3, but there's an alternative: the static factory method create(). It lets you instantiate objects in a lazy fashion, which should lead to better performance and it allows for the chaining of methods, like:

$myobj = MyObject::create(...)->setSomeValue($value);

(Chaining now works in core, but for it to work in your own modules, you'd have to make sure your objects methods return $this)

A singleton object is special in that it is only instantiated once. if you try to instatiate it again, it returns a reference to itself. Use it in situations where you don't want/need to create multiple instances of the same class. Google for examples. This is basically it, not really a mess.

As for the Injector class, to which I think you're also referring:
http://doc.silverstripe.org/framework/en/3.1/reference/injector

This is a special class, targeted at far more then just creating a simple object. So although you could use its create/get methods to create objects/singletons, you probably wouldn't, if that is all you were going to do. As for creating Sevices/using a Factory - I guess that's far beyond the scope of simply instantiating objects :)

I hope this answers some of your questions

Martine

Avatar
Bonner

Community Member, 21 Posts

4 June 2014 at 6:02am

Hi martimiz

Thanks for your response, it confirms what I thought.

So that said, can we look at the theory and logic behind each method.

I'll start with DI, you said:
"So although you could use its create/get methods to create objects/singletons, you probably wouldn't."

My question is why wouldn't you?

As you have made a statement about two different subjects, I will cover each within their own merit from my point of view.

You wouldn't use DI to create an object, because if I'm not mistaken, autoloading takes care of dependency injection, meaning you can just create a new instance of a class like so:

$myobj = new MyObject;

You wouldn't use DI to create a singleton because instead of using singletons you would just ensure encapsulation to start with.

So the Injector class is obsolete in my view.

Chaining is a neat feature, but it is badly abused. In my view, a method should return a value produced by the method and in relation to what the method has just done. If a method does one thing AND another thing, this is a code smell. A method doing something AND returning an instance of the object is a code smell.

Chaining should be used to pass objects that are requested by calling the method, take the following example:

class SessionStateHandler {
public function save () {
// xxx save the session
}
}

class HttpContext {
protected $session; // Instance of SessionStateHandler;

public function getSession () {
return $this->session;
}
}

class HttpApplication {
protected $context; // Instance of HttpContext

public function getContext () {
return $this->context;
}
}

$application = new HttpApplication;
$session = $application->getContext()->getSession();
$session->save();

I can't see how using a factory leads to better performance over the standard convention of the following so can you please explain how it does:

$myobj = new MyObject;

Do you get where I am going? If I'm not making sense please say, as I'm trying to dumb down the examples and explanations to make my points more concise.

Avatar
Marcus

Administrator, 87 Posts

4 June 2014 at 4:40pm

So the 4 main mechanisms you've mentioned

1. Using the singleton function

Only use this when you want to retrieve the same global instance of a particular object used everywhere else; Given the 'global' nature of it, it's probably best to avoid using it if you can. Using the dependency injector to instead wire in a dependency would be preferable.

It's effectively shorthand for doing $injector->get('Object');

2. Using dependency injection

The dependency injector handles the creation of the object, and subsequent creation/association of any dependent objects into place

3. Using a Factory

Using the MyObject::create($param) notation is effectively just a shortcut for using $injector->createWithArgs('MyObject', $param), so is using the injector under the covers.

4. Autoloading

By "autoloading" are you referring to PHP's __autoload mechanism? This doesn't have anything to do with the dependency injector at all. If you create an object using `new MyObject()`, none of its defined dependencies will be wired up.

Basically, methods 1 and 3 are shortcuts to the underlying dependency injection functionality.

Avatar
Bonner

Community Member, 21 Posts

4 June 2014 at 10:33pm

Edited: 04/06/2014 10:59pm

Globals are very dangerous, in my view they should not be used at all when developing applications that will be used in production environments.

PHP provides autoloading, so why reinvent the wheel by writing an Injector class?

"Using the MyObject::create($param) notation is effectively just a shortcut for using $injector->createWithArgs('MyObject', $param), so is using the injector under the covers."

If this is the case then I've already pointed out why I think that this is pointless due to autoloading.

"By "autoloading" are you referring to PHP's __autoload mechanism? This doesn't have anything to do with the dependency injector at all. If you create an object using `new MyObject()`, none of its defined dependencies will be wired up."

Autoloading ensures the class is available for use so we don't need to use composer which then allows for native DI like so...

class SessionStateHandler {
public function save () {
// xxx save the session
}
}

class HttpContext {
protected $session; // Instance of SessionStateHandler;

public function __construct (SessionStateHandler $session) {
$this->session = $session;
}
}

class HttpApplication {
protected $context; // Instance of HttpContext

public function __construct (HttpContext $context) {
$this->context = $context;
}
}

$session = new SessionStateHandler;
$context = new HttpContext($session);
$application = new HttpApplication($context);

What is wrong with the above? What is the Injector class trying to achieve, what problems is it solving, etc.? Maybe including an example of why you would use the Injector class over the above might help explain what the point of it is because at the moment it isn't making much sense at all. The documentation on what the Injector class does isn't very clear either because the examples don't present a problem, and how the Injector class solves the problem.

So in my view, the singleton function is obsolete and should be deprecated. The Factory should be deprecated, and we should only use one standard way of achieving DI, and at the moment I can't really see a case for using the Injector class.

Avatar
Bonner

Community Member, 21 Posts

11 June 2014 at 2:40am

In an effort to find harmony and peace of mind, are there any more thoughts on this?