设计模式SOLID - 开闭原则

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式SOLID - 开闭原则相关的知识,希望对你有一定的参考价值。

The Open – Closed principle have been formed by Bertrand Meyer in 1988, it can be paraphrased as:

Software entities (let it be classes, modules, functions, etc.) should be open for extending and closed for modification. 

This simple rule means that when we need to change or modify behaviour of our application we should be able to do so not by modifying existing code, but by writing new code instead. 

In php it is very easy to break that rule. You always work with code that you can easily modify. Just open a file, whether it is your own or one coming from some third-party library, make your changes – and it done!. PHP offers no formal means of enforcing that rule. 

In other languages, such as C# or C++ you’re often required to work with classes and code closed in libraries. They are compiled to DLLs, you cannot change single line of code in them. 

Make no mistake here, compiling to DLLs does not automatically means you are following open-closed principle. With good object orientated design even when you get the code used to compile DLLs, you should not need to modify it to change your application behaviour. As said earlier following open-closed principle means that changes in your application are achived by writing new code, not modifying existing code-base.

We’ll try to explain open-closed principle with a simple example, which could be used as a starting point in real-world situation. 

In modern web application we’re often required to display images generated on-the-fly. Before image is send to browser we need to state what kind of image we’re sending. 

This is achieved by sending appropriate Content-Type header. For an GIF image we would send :

header("Content-type: image/gif");

for a jpg we send

header(‘Content-type: image/jpg’);

We could write a simple class that does that for us. (Please note that many aspects of sending file content to browser are omitted here for clarity sake.) 

class Image
{

    private $fPath = null;

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

    private function GetFileExtention()
    {
        $strFileName = basename($this->fPath);

        $strExtension = array_pop(explode(".", $strFileName));
    
        return $strExtension;
    }


    public function SendToBrowser()
    {
        $strFileExtention = $this->GetFileExtention();

        switch ( $strFileExtention )
        {
            case ‘gif’ :

            header("Content-type: image/gif");

            break;

            case ‘jpg’ :

            header("Content-type: image/jpg");

            break;
        }

        $strFileContent = file_get_contents($this->fPath);

        echo $strFileContent;

        die();
    }
}     

Task done! We have a reusable class that can be used to send gifs or jpgs to the browser. But this class is not following the principle. 

Why this class breaks the open-closed rule? If we need to send PNG image, we would need to change behaviour of SendToBrowser method and add new case to our switchstatement.

The easiest way to stick with the principle is to create abstraction. We need to separate what might vary with what stays constant. In our example the switch statement is the varying part. Let’s try to abstract our Image class.

abstract class AnImage
{
    public function __construct($fPath)
    {
        $this->fPath = $fPath;
    }

    public function SendToBrowser()
    {
        $this->SendFileHeader();

        $strFileContent = file_get_contents($this->fPath);

        echo $strFileContent;

        die();
    }

    public abstract function SendFileHeader();
}     

First thing we notice is that our abstract class is a lot simpler. We’re able to get rid of uglyGetFileExtenstion method. We’ve created a much nicer code. Before file is send to browser we call SendFileHeader method. AnImage class does not provide implementation for it, hence it is declared abstract. Concrete classes need to provide implementation for this method. 

Now it is time to create out concrete classes, each representing different file type:

class JpgImage extends AnImage
{
    public function SendFileHeader()
    {
        header("Content-type: image/jpg");
    }
}


class GifImage extends AnImage
{
    public function SendFileHeader()
    {
        header("Content-type: image/gif");
    }
}

In concrete classes we’re implementing abstract SendFileHeader method, sending header appropriate for their file types.

We’ve just created a design that follows open-closed principle. If we need to send images of different types we will create new concrete classes that will implement our abstractSendFileHeader method. Changing our application resulted in writing new code and not changing existing classes. Using similiar approch we can build upon our example to have a desing that will allow us to send any kind of file to a browser.  

Our first implementation of Image class did not follow one other object orientated design principle. The Single Responsibility Principle. But we leave this till we meet again.

以上是关于设计模式SOLID - 开闭原则的主要内容,如果未能解决你的问题,请参考以下文章

如何应用 SOLID 原则在 React 中整理代码之开闭原则

利用开闭原则 (SOLID)

SOLID - 违反开闭原则

开放封闭原则|SOLID as a rock

SOLID六大设计原则总结

LARAVEL:如何使用 SOLID 原则的开闭原则?