一個類只做一件事

minororange發表於2019-01-28

說明

本文主要結合程式碼說明如何去在寫程式碼時做到 一個類只做一件事

需求

實現一個 cURL 類,針對不同平臺的 API 進行操作,並最終返回 array 給方法的呼叫方。

思路

分析需求設計到的功能點:

  • cURL 操作類
  • 不同平臺的驗證規則可能不一樣,所以每一個平臺都應該對應一個類
  • cURL 請求一般返回的是字串,需要將結果轉化為 array

實現

  1. 定義好 CurlInterfaceDriver 類的 Interface 規範。

    主要定義三個常用方法

    namespace App\Curl\bin;
    
    interface CurlInterfaceDriver
    {
        public function get($url, $options = []);
    
        public function post($url, $options = []);
    
        public function request($method, $url, $options = []);
    }
  2. 實現抽象類 AbstractHttpCurlDriver

    namespace App\Curl\bin;
    
    use GuzzleHttp\Client;
    
    abstract class AbstractHttpCurlDriver implements CurlInterfaceDriver
    {
        /**
         * @var null|Client
         */
        protected $client = null;
    
        protected $response = null;
    
        public function __construct(Client $client = null)
        {
            if (is_null($client)) {
                $client = new Client();
            }
    
            $this->setClient($client);
        }
    
        /**
         * @param $url
         * @param array $options
         * @return string
         * @date 2019/01/25
         * @author ycz
         * @throws \GuzzleHttp\Exception\GuzzleException
         */
        public function get($url, $options = [])
        {
            $response = $this->getClient()->request('GET', $url, $options)->getBody()->getContents();
    
            $this->setResponse($response);
    
            return $response;
        }
    
        /**
         * @param $url
         * @param array $options
         * @return string
         * @date 2019/01/25
         * @author ycz
         * @throws \GuzzleHttp\Exception\GuzzleException
         */
        public function post($url, $options = [])
        {
            $response = $this->getClient()->request('POST', $url, $options)->getBody()->getContents();
    
            $this->setResponse($response);
    
            return $response;
        }
    
        /**
         * @param $method
         * @param $url
         * @param array $options
         * @return string
         * @date 2019/01/25
         * @author ycz
         * @throws \GuzzleHttp\Exception\GuzzleException
         */
        public function request($method, $url, $options = [])
        {
            $response = $this->getClient()->request($method, $url, $options)->getBody()->getContents();
    
            $this->setResponse($response);
    
            return $response;
        }
    
        /**
         * @return null|Client
         */
        private function getClient()
        {
            return $this->client;
        }
    
        /**
         * @param Client $client
         */
        private function setClient(Client $client)
        {
            $this->client = $client;
        }
    
        /**
         * @return null|string
         */
        public function getResponse()
        {
            return $this->response;
        }
    
        /**
         * @param null|string $response
         */
        private function setResponse($response)
        {
            $this->response = $response;
        }
    }
  3. 每個平臺返回資料格式可能不一樣,所以抽象類中只將結果字串返回,針對不同返回資料格式,我們可以建立不同的 Driver 繼承 AbstractHttpCurlDriver

    針對 JSON 返回結果的類。

    namespace App\Curl\bin;
    
    class JsonHttpCurlDriver extends AbstractHttpCurlDriver
    {
    
    }

    針對 XML 返回結果的類。

    namespace App\Curl\bin;
    
    class XMLHttpCurlDriver extends AbstractHttpCurlDriver
    {
    
    }

    因為 JSONXML 兩個驅動的只有返回結果不一樣,其他內容基本一樣,所以不用在自己類裡面改寫抽象類裡面的東西。

  4. 實現將 response 轉換為 array 的類

    根據使用的 driver 自動轉換結果。

    namespace App\Curl\bin;
    
    use App\Exceptions\ResponseNotJsonException;
    use App\Exceptions\ResponseNotXMLException;
    use SimpleXMLElement;
    
    class ApiData2ArrayFactory
    {
        public static function make(CurlInterfaceDriver $curl)
        {
            if ($curl instanceof JsonHttpCurlDriver) {
                return static::json2Array($curl->getResponse());
            }
    
            if ($curl instanceof XMLHttpCurlDriver) {
                return static::xml2Array($curl->getResponse());
            }
        }
    
        /**
         * json 轉陣列
         *
         * @param $json
         * @return array
         * @date 2019/01/25
         * @author ycz
         * @throws ResponseNotJsonException
         */
        private static function json2Array($json)
        {
            try {
                $data = \GuzzleHttp\json_decode($json, true);
            } catch (\InvalidArgumentException $e) {
                throw new ResponseNotJsonException();
            }
    
            return $data;
        }
    
        /**
         * @param $xml
         * @return array
         * @date 2019/01/25
         * @author ycz
         * @throws ResponseNotXMLException
         */
        private static function xml2Array($xml)
        {
            if (!static::isXml($xml)) {
                throw new ResponseNotXMLException();
            }
            function _XML2Array(SimpleXMLElement $parent)
            {
                $array = array();
    
                foreach ($parent as $name => $element) {
                    ($node = &$array[$name])
                    && (1 === count($node) ? $node = array($node) : 1)
                    && $node = &$node[];
    
                    $node = $element->count() ? _XML2Array($element) : trim($element);
                }
    
                return $array;
            }
    
            $xml = new SimpleXMLElement($xml);
            $data = _XML2Array($xml);
    
            return $data;
        }
    
        /**
         * @param $xml
         * @return int
         * @date 2019/01/25
         * @author ycz
         */
        private static function isXml($xml)
        {
            return xml_parse(xml_parser_create(), $xml, true);
        }
    }
  5. 實現對外呼叫的 cURL

    namespace App\Curl\bin;
    /**
     * Class Curl
     *
     * @package App\Curl\bin
     * @method get
     * @method post
     * @method request
     * @see AbstractHttpCurlDriver
     */
    class Curl
    {
        private $curlDriver = null;
    
        public function __construct(CurlInterfaceDriver $curl)
        {
            $this->curlDriver = $curl;
        }
    
        public function __call($name, $arguments)
        {
            $this->curlDriver->$name(...$arguments);
    
            return ApiData2ArrayFactory::make($this->curlDriver);
        }
    }
  6. 實現單個平臺的 cURL 類,以亞馬孫為例,我們建立一個 AmazonCurl

    namespace App\Curl\Amazon;
    
    use App\Curl\BaseCurl;
    use App\Curl\bin\Curl;
    use App\Curl\bin\JsonHttpCurlDriver;
    
    class AmazonCurl extends BaseCurl
    {
    
        public function __construct()
        {
            $this->curl = new Curl(new JsonHttpCurlDriver());//假設亞馬遜返回的是 json 資料
        }
    
        public function getProductList()
        {
            return $this->curl->get('http://amazon.com/get-product-list',[]);
        }
    }
  7. 外部呼叫

    $amazonCurl = new AmazonCurl();
    
    return $amazonCurl->getProductList();
  8. 說明

    如果哪天亞馬遜的介面返回變成了 XML 資料,我們只用將 AmazonCurl 建構函式中的 JsonHttpCurlDriver 替換為 XMLHttpCurlDriver 即可。

    後續如果新增了其他格式的資料返回,支援新資料只用實現兩個東西:1. 繼承於 AbstractHttpCurlDriver 的驅動類。2. 在 ApiData2ArrayFactory 實現改格式轉 array 的方法。

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章