简体   繁体   中英

How to use an abstract class instead of an interface for a custom component?

I'm working on a File Manager module for Yii2 where the goal is to provide the user with an option to use different sources for directory browsing, like

  • Local Directory
  • Dropbox
  • One Drive
  • Google Drive

To accomplish this, I wrote an interface which will be implemented by different adapters that the user wants to use. For example, here are a few lines of code from the interface with common functions:

<?php

namespace buttflattery\filemanager\adapters;

/**
 * File Manager Interface class
 */
interface DirectoryInterface
{

    public function listFolder(string $directory, array $sortParams);

    public function removeFile(string $file);

    public function createFolder(string $folder, string $parentDirectory);

    public function uploadFile(string $file, string $destinationDirectory);

    public function downloadFile(string $zipFile);

    public function renameFile(string $oldFile, string $newFile);

    public function moveFiles(array $filesList, string $destination);

    public function copyFiles(array $files, string $destination);

}

I started with the LocalDirectoryAdapter ; it's attached as a component with the module via a config file,

'modules' => [
    'filemanager' => [
        'class' => 'buttflattery\filemanager\Module',
        'components' => [
             'adapter' => [
                 'class' => 'buttflattery\filemanager\adapters\local\LocalDirectoryAdapter',
                 'rootDir' => 'tinyii-uploads'
             ]
        ],
    ],
],

It all works fine, until I came across a situation after I started implementing the Dropbox adapter.

There are a couple of methods that will be using exactly the same code. For instance, the listFolder() is the same in both LocalDirectoryAdapter and DropoxAdapter .

The reason is because there is a FileIndexer component which queries the file tree and writes it down to a JSON file, and then all the listing and browsing is done via that JSON file by the FileIndexer component.

At that point, I figured I needed to change the interface to an abstract class, add those methods that are common, and leave those methods as abstract which will need to be implemented specifically to the Directory source I am using. That is where I got puzzled:

The LocalDirectoryAdapter class is declared as

class LocalDirectoryAdapter extends Component implements DirectoryInterface{
}

Once I change the interface to an abstract class, I am bound to use extend , and in that case, I cant extend the yii\base\Component class.

How can I get around this problem?

You can make your abstract adapter extend the yii\base\Component then by extending your abstract adapter the child class will also inherit from Component.

For example like this

abstract class BaseDirectoryAdapter extends Component
{
}

class LocalDirectoryAdapter extends BaseDirectoryAdapter
{
}

But I think it's not a good practice to force usage of your abstract base class. You don't know what adapters might be implemented in future.

What you can do is to use both. The interface that would define minimal requirements for adapter and abstract base class that would provide standard impelmentations for common methods.

interface DirectoryInterface
{
}

abstract class BaseDirectoryAdapter extends Component implements DirectoryInterface
{
}

class LocalDirectoryAdapter extends BaseDirectoryAdapter
{
}

This allows you to benefit from implementations in BaseDirectoryAdapter but doesn't force you to use it.

Yii uses this approach, you can for example see it with yii\db\ActiveRecord , yii\db\BaseActiveRecord and yii\db\ActiveRecordInterface .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM