开发者

Program to an interface not an implementation in php

One of the m开发者_C百科ajor design principles is program to an interface not an implementation. Is this even possible in php or any other weakly typed language.

EDIT:

I maybe didnt write the question as clearly as i should have. I dont mean that php cant use interfaces - it obvisouly can. I mean does the design principle "program to an interface and not an implementation" become redundant in weakly typed languages.


Yes. Define the interface:

interface iTemplate
{
    public function setVariable($name, $var);
    public function getHtml($template);
}

And implement it:

// Implement the interface
class Template implements iTemplate
{
    private $vars = array();

    public function setVariable($name, $var)
    {
        $this->vars[$name] = $var;
    }

    public function getHtml($template)
    {
        foreach($this->vars as $name => $value) {
            $template = str_replace('{' . $name . '}', $value, $template);
        }

        return $template;
    }
}

PHP manual on Interfaces: http://php.net/manual/en/language.oop5.interfaces.php

I don't know why it wouldn't be possible to have interfaces just because the language is weakly typed.


EDIT: The point (more or less) of having an interface is so you can re-use your code regardless of the class that actually implements said interface.

Say your program uses an interface Set, which has methods addItem(), removeItem() and contains(). With interfaces, you know you'll be able to call any of these 3 methods regardless of the underlying Set implementation, be it HashSet, TreeSet or whatever.

This doesn't change if you are using a weakly typed language; you can still code as if you were using a strongly typed language. I know I didn't word this explanation really well, but I hope you get the idea.


php has interfaces and you can program to them. Why shouldn't you be able to do that?

Programming to an interface means that you just use the functionality that is offered by the interface and neither rely on implementation details nor use other functionality that is offered by the implementation and you just happen to know about it, because the implementation might change (the interface shouldn't).


Depends on what you mean by "interface" and "implementation". These are loose terms whose meanings can shift depending on context.

PHP5 contains OOP constructs similar to Java and C#, such as Objects as references, Classes, Abstract classes and Interfaces. It also contains type hinting for method paramaters. These tools could be, and have been, used to build "an interface" for something.


The ultimate goal is to have an interface which each component agrees upon.

So, if, for example, I was building a JavaScript site that was done -entirely- in an old-school MVC (non-Rails/PHP) implementation, and completely in AJAX, I would make sure that each of the components implemented the same interface for observing.

In each model/view/controller, I could name my "subscribe" method something completely different. Or, I could implement a standard interface for each.

So I can have a public ".Register(event_type, subscribing_class)" method implemented in EVERY single component which could be expected to be called.

Likewise, I can have a public ".Update(event_type, data)" method implemented in EVERY single component which could be expected to be called.

The .Register and .Update are the interface for my Observer communication. Inside of my classes, each might have a ".Subscribe(publisher, event_type, self_reference)" method. That method might just be:

Class.Subscribe = function (publisher, event_type) {
    var self_reference = this;
    publisher.Register(event_type, self_reference);
};

Each might have an internal .Notify method:

Class.Notify = function (type, data) {
    var subscribers = this.subscribers[type],
        i = 0, l = subscribers.length;

    for (; i < l; i++) { subscribers[i].Update(type, data); }
};

Because I've agreed that ALL of my communication interface is going to behave this way, it doesn't matter what my internals look like.

My Model can continue to be oblivious to my View, and my View can continue to be oblivious to my Controller.

The .Notify and .Subscribe don't need to be implemented that way - they aren't a part of the publicly-accessible interface. They could be anything I want.

.Subscribe could take an ARRAY of publishers and push it through a for-loop, to subscribe to multiple points of data. Or take an array of { "pub" : x, "type" : y } object literals, and call the .Register method of each one, so that you can get ALL bootstrapping of that Class out of the way with one function call.

Same thing goes with making an audio-player app. I don't care what public properties MusicPlayer shares. I know that it uses .Play(), .Pause(), .Stop(), .Load(track).

If I make sure that I ONLY use the agreed-upon, public interface methods, the program is going to work. Why?

Because the guy who works on MusicPlayer might change the internals of MusicPlayer. He might completely rewrite them. Maybe there's a ._precacheSong(track) method. But what if it gets replaced with ._cueTrack(track) down the road?

You're just using some dude's widget, and one day your widget crashes, because you were extending it or implementing it based on non-interface methods or non-interface data, which happened to change as of v1.2.1

So even in Loose Languages, the Interface is important. They give you a map of how you can expect to call ANY component that is expected to have that functionality - and regardless of how the internals of that component work, the inputs and outputs will be the exact same (though more type/error-checking is needed on inputs).

They allow you to "Duck-Type" really easily (take a list of different Class instances - fire off the same function call on each one, expecting each has the same method and takes the same data-format).

Even better with JavaScript:

My .Subscribe code might even be written only once, and then bound to anything that I want to "inherit" it.

Interface.Subscribe = function (publisher, evt_type) {
    var self_ref = this;
    publisher.Register(evt_type, self_ref);
};

Class_1.Subscribe = Interface.Subscribe.bind(Class_1);
Class_2.Subscribe = Interface.Subscribe.bind(Class_2);
Class_3.Subscribe = Some_Other_Interface.Subscribe.bind(Class_3);

And I can do that freely, because I know that everything I want to subscribe to is going to have the same public-interface.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜