理解Flight框架核心

看到了這篇分析flight的文章還不錯,就轉過來了,地址:https://blog.csdn.net/sky_zhe/article/details/38906689


?

Flight框架(官網)是一個微型的PHP框架,它簡單,快速,可擴展。借助Flight可以快捷而輕松的創建你的RESTFul web應用。

雖然是一個微型的框架,而且代碼量確實也較少,但我在閱讀Flight代碼的過程中,感到了它設計和構思獨特而精妙的地方,覺得有學習的價值,便決定做一下整理分享出來。

如果你對框架還不熟悉,可以先去官網看下文檔,如果需要中文文檔的話可以可以點這里。

如果你已經對Flight有一定了解了,接下來就來看看Flight是怎么工作的吧。

<?php  
class Flight {  /** * Framework engine. * @var object */  private static $engine;  // Don't allow object instantiation  private function __construct() {}  private function __destruct() {}  private function __clone() {}  /** * 之前已經看到了,框架內所有函數都是以Flight類的靜態函數形式調用的 * __callStatic()這個魔術方法能處理所有的靜態函數 * @param string $name Method name * @param array $params Method parameters * @return mixed Callback results */  public static function __callStatic($name, $params) {  static $initialized = false;  if (!$initialized) {  //這里定義框架的自動加載機制,實際上是依據PSR-0標準來做的  require_once __DIR__.'/autoload.php';  //Engine類是框架的引擎所在  self::$engine = new \flight\Engine();  $initialized = true;  }  //在這里,Flight對Engine包裝了一層而已。對Flight類靜態函數的調用,實質上是對Engine類的相應函數的調用  return \flight\core\Dispatcher::invokeMethod(array(self::$engine, $name), $params);  }  
}  //那么就直接就來看看Dispatcher::invokeMethod函數吧  
namespace flight\core;  
class Dispatcher {  /** * 調用一個方法 * @param mixed $func Class method * @param array $params Class method parameters * @return mixed Function results */  public static function invokeMethod($func, array &$params = array()) {  list($class, $method) = $func;  $instance = is_object($class);  switch (count($params)) {  case 0:  return ($instance) ?  $class->$method() :  $class::$method();  case 1:  return ($instance) ?  $class->$method($params[0]) :  $class::$method($params[0]);  case 2:  return ($instance) ?  $class->$method($params[0], $params[1]) :  $class::$method($params[0], $params[1]);  case 3:  return ($instance) ?  $class->$method($params[0], $params[1], $params[2]) :  $class::$method($params[0], $params[1], $params[2]);  case 4:  return ($instance) ?  $class->$method($params[0], $params[1], $params[2], $params[3]) :  $class::$method($params[0], $params[1], $params[2], $params[3]);  case 5:  return ($instance) ?  $class->$method($params[0], $params[1], $params[2], $params[3], $params[4]) :  $class::$method($params[0], $params[1], $params[2], $params[3], $params[4]);  default:  return call_user_func_array($func, $params);  }  }  
}  

?

上面注釋里提到了,自動加載和PSR-0,我之前寫過一篇關于這部分內容的文章。Flight框架的自動加載就是基于namespace和psr-0標準的:

?

//只列出有關自動加載部分的主要代碼  
namespace flight\core;  
class Loader {  /** * Starts/stops autoloader. * * @param bool $enabled Enable/disable autoloading * @param mixed $dirs Autoload directories */  public static function autoload($enabled = true, $dirs = array()) {  if ($enabled) {  spl_autoload_register(array(__CLASS__, 'loadClass'));  }  else {  spl_autoload_unregister(array(__CLASS__, 'loadClass'));  }  if (!empty($dirs)) {  self::addDirectory($dirs);  }  }  /** * Autoloads classes. * * @param string $class Class name */  public static function loadClass($class) {  $class_file = str_replace(array('\\', '_'), '/', $class).'.php';  foreach (self::$dirs as $dir) {  $file = $dir.'/'.$class_file;  if (file_exists($file)) {  require $file;  return;  }  }  }  
}  

?

?

再繼續往下看之前,我們不妨先對Flight內主要的類和函數進行一下梳理,以下是Flight框架的內置類:

?

  • Engine類:包含了這個框架的核心功能。它的責任是加載HTTP請求,運行已注冊的服務,并生成最后的HTTP響應。
  • Loader類:它負責框架內對象的加載。用自定義的初始化參數來生成新的類實例,并且維護可復用的類實例的列表。它還處理剛才提到過的類的自動加載。
  • Dispatcher類:它負責框架內事件的分發處理。事件即是對類方法或函數的簡單的稱呼(別名)。它還允許你在事件上的掛鉤點掛載別的函數,能夠改變函數的輸入或者輸出。
  • Router類:它負責將一個HTTP講求發送到指定的函數進行處理。它視圖將請求的URL和一系列用戶定義的URL范式進行匹配。
  • Route類:它負責路由的具體實現。Router相當于對Route的包裝。
  • Request類:它代表了一個HTTP請求。所有來自$_GET,$_POST,$_COOKIE,$_FILES中的數據都要通過Request類獲取和訪問。默認的Request屬性就包括url,base,method,user_agent等。
  • Response類:對應于Request,它代表了一個HTTP響應。這個對象包括了返回頭,HTTP狀態碼和返回體。
  • View類:視圖類負責將輸出展示。它提供了在渲染時管理視圖數據和將數據插入視圖模板的函數。
  • Collection類:它允許你既可以以使用數組的方式,也能以使用對象的方式來訪問數據。

?

Flight框架的函數分兩部分,一部分是核心函數:

?

Flight::map($name, $callback) // Creates a custom framework method.  
Flight::register($name, $class, [$params], [$callback]) // Registers a class to a framework method.  
Flight::before($name, $callback) // Adds a filter before a framework method.  
Flight::after($name, $callback) // Adds a filter after a framework method.  
Flight::path($path) // Adds a path for autoloading classes.  
Flight::get($key) // Gets a variable.  
Flight::set($key, $value) // Sets a variable.  
Flight::has($key) // Checks if a variable is set.  
Flight::clear([$key]) // Clears a variable.  

?

另一部分是擴展函數:

?

Flight::start() // Starts the framework.  
Flight::stop() // Stops the framework and sends a response.  
Flight::halt([$code], [$message]) // Stop the framework with an optional status code and message.  
Flight::route($pattern, $callback) // Maps a URL pattern to a callback.  
Flight::redirect($url, [$code]) // Redirects to another URL.  
Flight::render($file, [$data], [$key]) // Renders a template file.  
Flight::error($exception) // Sends an HTTP 500 response.  
Flight::notFound() // Sends an HTTP 404 response.  
Flight::etag($id, [$type]) // Performs ETag HTTP caching.  
Flight::lastModified($time) // Performs last modified HTTP caching.  
Flight::json($data, [$code], [$encode]) // Sends a JSON response.  
Flight::jsonp($data, [$param], [$code], [$encode]) // Sends a JSONP response. 

?

?

Flight框架的使用方式就是對Flight類靜態函數調用(Flight::func()),我們在上面提到過,其實質是對Engine對象中函數的調用($engineObj->func())。

?

而Engine類的函數有兩類,一類是核心函數,是直接進行調用(相對于動態調用)的,另外的擴展函數,則是進行動態調用的。

?

此外,在Flight中加載類和資源,獲得某個類的實例,直接調用Flight::className()即可,等同于$engineObj->className()。這個也是采用動態調用的形式。也就是說,除了Engine類的核心函數,其他函數(類)都是動態調用的。這樣,框架就可以為此提供一個統一的入口了。

?

namespace flight;  
class Engine {  //....  public function __construct() {  $this->vars = array();  //上面提到過,Flight中,Dispatcher負責處理函數,Loader負責對象的加載  $this->loader = new Loader();  $this->dispatcher = new Dispatcher();  $this->init();  }  /** * __call是一個魔術方法,當調用一個不存在的函數時,會調用到該函數 * 剛才講的動態調用就是通過這個函數進行的 * @param string $name Method name * @param array $params Method parameters * @return mixed Callback results */  public function __call($name, $params) {  //先判斷是類還是可直接調用的函數  $callback = $this->dispatcher->get($name);  //如果是函數,通過dispatcher處理  if (is_callable($callback)) {  return $this->dispatcher->run($name, $params);  }  //是否是共享實例  $shared = (!empty($params)) ? (bool)$params[0] : true;  //通過loader加載該類的對象  return $this->loader->load($name, $shared);  }  /** * 框架初始化 */  public function init() {  static $initialized = false;  $self = $this;  if ($initialized) {  $this->vars = array();  $this->loader->reset();  $this->dispatcher->reset();  }  // Flight中,類會通過loader的register函數進行注冊  // 注冊默認組件  $this->loader->register('request', '\flight\net\Request');  $this->loader->register('response', '\flight\net\Response');  $this->loader->register('router', '\flight\net\Router');  $this->loader->register('view', '\flight\template\View', array(), function($view) use ($self) {  $view->path = $self->get('flight.views.path');  });  // 注冊框架方法  $methods = array(  'start','stop','route','halt','error','notFound',  'render','redirect','etag','lastModified','json','jsonp'  );  // Flight中,method會通過dispatcher的set函數將對應的回調函數綁定到一個事件中  // 為了可以進行動態調用,Enginge的擴展函數全部是通過 _method 的名字定義的  foreach ($methods as $name) {  $this->dispatcher->set($name, array($this, '_'.$name));  }  // 默認的配置  $this->set('flight.base_url', null);  $this->set('flight.handle_errors', true);  $this->set('flight.log_errors', false);  $this->set('flight.views.path', './views');  $initialized = true;  }  /** * 將一個類注冊到框架方法中,我們就是通過這個函數注冊我們自定義的類的 *  * @param string $name Method name * @param string $class Class name * @param array $params Class initialization parameters * @param callback $callback Function to call after object instantiation * @throws \Exception If trying to map over a framework method */  public function register($name, $class, array $params = array(), $callback = null) {  if (method_exists($this, $name)) {  throw new \Exception('Cannot override an existing framework method.');  }  //通過loader的register函數進行注冊  $this->loader->register($name, $class, $params, $callback);  }  /** * 將一個回調函數映射到框架方式中,我們就是通過這個函數映射我們自定義函數的 * * @param string $name Method name * @param callback $callback Callback function * @throws \Exception If trying to map over a framework method */  public function map($name, $callback) {  if (method_exists($this, $name)) {  throw new \Exception('Cannot override an existing framework method.');  }  //會通過dispatcher的set函數將對應的回調函數綁定到一個事件中  $this->dispatcher->set($name, $callback);  }  //...  
}  

?

Flight中的兩個核心函數,map是映射自定義的函數,最后是通過dispathcer的set函數實現的,register是注冊自定義的類,最后是通過loader的register函數實現的。而框架自己的核心組件和擴展函數,Engine在初始化過程幫我們完成了這兩個過程。接著Flight提供了一個統一的入口,可以動態調用所有非核心的函數,類。這就是Flight的核心加載機制了。

?

可能你還有疑問,為什么Flight要使用動態調用的形式去訪問這些函數或對象?尤其是對于Engine的擴展函數,為什么不直接進行調用呢?因為Flight可以對它們進行過濾或重寫。過濾和重寫是Flight框架進行擴展的重要功能。框架實現了統一的資源操作方式后,就可以方便的進行重寫或者過濾的處理了。需要注意的是,核心函數諸如map和register是不能夠進行過濾或重寫的,相信你已經清楚為什么了。

?

框架的重寫功能還是使用的map和register這兩個函數。這個功能因為框架的設計方式,很輕易的完成了。在Dispatcher和Loader中都動態維護了一個映射表,Dispatcher里是回調到事件的映射,Loader中是class到實例和構造函數等的映射。這樣,注冊自定義函數或類時,遇到一樣名字就會覆蓋掉之前的,而使用時只返回最新的。下面是Loader類的部分代碼:

?

namespace flight\core;  
class Loader {  //....  /** * 注冊一個類 * * @param string $name Registry name * @param string|callable $class Class name or function to instantiate class * @param array $params Class initialization parameters * @param callback $callback Function to call after object instantiation */  public function register($name, $class, array $params = array(), $callback = null) {  unset($this->instances[$name]);  $this->classes[$name] = array($class, $params, $callback);  }  /** * 加載一個已注冊的類 * * @param string $name Method name * @param bool $shared Shared instance * @return object Class instance */  public function load($name, $shared = true) {  $obj = null;  //$this->classes是注冊過的類  //$this->instances是加載過的實例  if (isset($this->classes[$name])) {  list($class, $params, $callback) = $this->classes[$name];  $exists = isset($this->instances[$name]);  //是不是共享實例  if ($shared) {  $obj = ($exists) ?  $this->getInstance($name) :  $this->newInstance($class, $params);  if (!$exists) {  $this->instances[$name] = $obj;  }  }  else {  $obj = $this->newInstance($class, $params);  }  if ($callback && (!$shared || !$exists)) {  $ref = array(&$obj);  call_user_func_array($callback, $ref);  }  }  return $obj;  }  /** * 得到一個類的單一實例 * * @param string $name Instance name * @return object Class instance */  public function getInstance($name) {  return isset($this->instances[$name]) ? $this->instances[$name] : null;  }  /** * 得到一個類的新的實例 * * @param string|callable $class Class name or callback function to instantiate class * @param array $params Class initialization parameters * @return object Class instance */  public function newInstance($class, array $params = array()) {  if (is_callable($class)) {  return call_user_func_array($class, $params);  }  switch (count($params)) {  case 0:  return new $class();  case 1:  return new $class($params[0]);  case 2:  return new $class($params[0], $params[1]);  case 3:  return new $class($params[0], $params[1], $params[2]);  case 4:  return new $class($params[0], $params[1], $params[2], $params[3]);  case 5:  return new $class($params[0], $params[1], $params[2], $params[3], $params[4]);  default:  $refClass = new \ReflectionClass($class);  return $refClass->newInstanceArgs($params);  }  }  //....  
}  

?

跟過濾器功能有關的函數是before和after,分別是在被過濾函數處理之前或之后進行操作。最終是在Dispatcher類中實現的。

?

?

namespace flight;  
class Engine {  /** * Adds a pre-filter to a method. * * @param string $name Method name * @param callback $callback Callback function */  public function before($name, $callback) {  $this->dispatcher->hook($name, 'before', $callback);  }  /** * Adds a post-filter to a method. * * @param string $name Method name * @param callback $callback Callback function */  public function after($name, $callback) {  $this->dispatcher->hook($name, 'after', $callback);  }  
}  namespace flight\core;  
class Dispatcher {  /** * 將回調注冊到一個事件之中 * * @param string $name Event name * @param callback $callback Callback function */  public function set($name, $callback) {  $this->events[$name] = $callback;  }  /** * 得到事件關聯的回調 * * @param string $name Event name * @return callback $callback Callback function */  public function get($name) {  return isset($this->events[$name]) ? $this->events[$name] : null;  }  /** * 在事件上掛一個回調函數 * * @param string $name Event name * @param string $type Filter type * @param callback $callback Callback function */  public function hook($name, $type, $callback) {  $this->filters[$name][$type][] = $callback;  }  /** * 對事件進行分發處理 * * @param string $name Event name * @param array $params Callback parameters * @return string Output of callback */  public function run($name, array $params = array()) {  $output = '';  // 運行前置過濾器  if (!empty($this->filters[$name]['before'])) {  $this->filter($this->filters[$name]['before'], $params, $output);  }  // 運行所請求的方法  $output = $this->execute($this->get($name), $params);  // 運行后置過濾器  if (!empty($this->filters[$name]['after'])) {  $this->filter($this->filters[$name]['after'], $params, $output);  }  return $output;  }  
}  

?


下面,還差最后一步,運行這個框架時處理流程是怎樣的呢?

?

namespace flight;  
class Engine {  /** * 啟動這個框架 */  public function _start() {  $dispatched = false;  $self = $this;  $request = $this->request();  $response = $this->response();  $router = $this->router();  // 沖刷掉已經存在的輸出  if (ob_get_length() > 0) {  $response->write(ob_get_clean());  }  // 啟動輸出緩沖  ob_start();  // 開啟錯誤處理  $this->handleErrors($this->get('flight.handle_errors'));  // 對AJAX請求關閉緩存  if ($request->ajax) {  $response->cache(false);  }  // 允許后置過濾器的運行  $this->after('start', function() use ($self) {  //start完成之后會調用stop()函數  $self->stop();  });  // 對該請求進行路由  while ($route = $router->route($request)) {  $params = array_values($route->params);  //是否讓路由鏈繼續下去  $continue = $this->dispatcher->execute(  $route->callback,  $params  );  $dispatched = true;  if (!$continue) break;  $router->next();  }  //路由沒找匹配到  if (!$dispatched) {  $this->notFound();  }  }  /** * 停止這個框架并且輸出當前的響應內容 * * @param int $code HTTP status code */  public function _stop($code = 200) {  $this->response()  ->status($code)  ->write(ob_get_clean())  ->send();  }  
}  


至此,應該對Flight核心的設計,功能以及處理流程有所認識了吧。至于其他的路由,請求已經響應等內容,就留給讀者自行學習吧。

轉載于:https://www.cnblogs.com/jiujuan/p/8874626.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/397448.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/397448.shtml
英文地址,請注明出處:http://en.pswp.cn/news/397448.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

安裝ISO系統(原版系統)系統終極方法

首先進入PE&#xff0c;在PE下找到你的系統ISO鏡像&#xff0c;解壓縮&#xff0c;然后將鏡像里的boot文件夾、sources文件夾和bootmgr文件提取出來&#xff0c;然后復制到你要安裝的分區&#xff08;比如c盤&#xff09;&#xff0c;接下來拔下U盤&#xff0c;重新啟動計算機&…

intel i218v千兆網卡 linux驅動,適用于英特爾? 千兆位以太網網絡連接的 Linux* 基礎驅動程序...

適用于英特爾 千兆位以太網網絡連接的 Linux* igb* 基礎驅動程序安裝說明Linux* igb 驅動程序支持所有基于 82575、82576、82580&#xff0c;I350&#xff0c;I354 和 I210/I211 的英特爾 千兆位以太網網絡連接。有關驅動程序配置的詳細信息&#xff0c;請參閱下載中心中的自述…

Linux下如何抓取串口碼流,linux alsa音頻中采樣率fs、比特率BCLK 、主時鐘MCLK關系...

轉&#xff1a;https://blog.csdn.net/lugandong/article/details/72468831一、拿512fs說話&#xff1a;看圖知道采樣的位深是32bit(位)&#xff0c;左右聲道各占了8*32BCLK&#xff0c;那一個完整的LRCLK一共8*32*2512BCLK。其實xxxfs就是這么算出來的&#xff0c;也是固定的&…

第 39 章 ThinkPHP--CURD 操作

學習ThinkPHP 模型中的 CURD 操作&#xff0c;也就是增刪改查。通過 CURD&#xff0c; 我們可以方便快速的對數據庫進行操作。 1.數據創建 2.數據寫入 3.數據讀取 4.數據更新 5.數據刪除 一&#xff0e;數據創建 在數據庫添加等操作之前&#xff0c;我們首先需要對數據進行創建…

洛谷 P1529 回家 Bessie Come Home Label:Dijkstra最短路 亂搞

題目描述 現在是晚餐時間,而母牛們在外面分散的牧場中。 農民約翰按響了電鈴,所以她們開始向谷倉走去。 你的工作是要指出哪只母牛會最先到達谷倉(在給出的測試數據中,總會有且只有一只最快的母牛)。 在擠奶的時候(晚餐前),每只母牛都在她自己的牧場上,一些牧場上可能沒有母牛。…

linux語言的說明順序有哪些,(linux常用頭文件詳解.doc

(linux常用頭文件詳解linux常用頭文件詳解POSIX標準定義的頭文件??????? 目錄項???????? 文件控制??? 文件名匹配類型??? 路徑名模式匹配類型??????? 組文件??? 網絡數據庫操作??????? 口令文件??? 正則表達式??????? TAR歸檔…

第 39 章 ThinkPHP--視圖

學習要點&#xff1a; 1.模版定義 2.賦值和渲染 3.模版地址 4.獲取內容 本節課&#xff0c;我們將要學習一下 ThinkPHP 視圖&#xff0c;視圖是 Web 的可見內容&#xff0c;一般是 HTML 結合 PHP 獲取的數據提供給用戶使用的部分&#xff0c;屬于 MVC 中的 V。 一&#xff0e;模…

mysql日志(介紹 路徑修改 備份)

2019獨角獸企業重金招聘Python工程師標準>>> 環境&#xff1a;senos6 軟件&#xff1a;mysql2.6.20 mysql日志&#xff1a; 錯誤日志 一般查詢日志 慢查詢日志 二進制日志 只記錄DDL&#xff0c;DML等引起數據庫改變的操作都會記錄下來 復制&am…

Sort

<?xml version"1.0" encoding"utf-8"?> SortSort 1 Sort Select sort is the simplest sorting alogrithms. 1.1 IDEA 1.find the smallest element in the rest of array 2.exchange the element with with the i th entry. 3.repeat step1 and s…

a標簽實現不跳轉點擊

<a class"tiao" href"./index.php"></a> JS實現無跳轉a標簽 <script type"text/javascript"> $(".tiao").click(function (){return false; }) </script> 轉載于:https://www.cnblogs.com/wenhainan/p/…

linux下的c語言控制燈閃爍,C語言實現LED燈閃爍控制

原標題&#xff1a;C語言實現LED燈閃爍控制/********* 配套 **********/#include //包含 寄存器的頭文件/****************************************函數功能&#xff1a;延時一段時間*****************************************/void delay(void) //兩個void意思分別為無需返回…

VBA and Access

>>.用vba連接ACESS&#xff1a; Set Conn Server.CreateObject("ADODB.Connection") Conn.ConnectionString"ProviderMicrosoft.Jet.OLEDB.4.0;Data Source" & Server.MapPath("sample.mdb") Conn.Open>>.用vba連接EXCEL,打開EX…

溫州大學c語言作業布置的網站,老師APP上布置作業 三年級娃為刷排名半夜做題_央廣網...

在溫州讀小學三年級的皮皮(化名)&#xff0c;因為學習需要&#xff0c;在媽媽黃女士的手機里安裝了5個APP學習軟件。有數學速算的&#xff0c;英語配音的&#xff0c;還有語文復習的。這些軟件&#xff0c;都是班上的老師推薦安裝的。每天放學回家&#xff0c;皮皮就拿著黃女士…

Algorithm I assignment Collinear

這本來應該是第三周的作業&#xff0c;但是由于其他作業逼近deadline&#xff0c;暫時推后了一周完成。 這周的assignment大大提高了我對這門課的看法&#xff0c;不得不說&#xff0c;Algorithms這門課的assignment部分設計得很好。為什么好&#xff1f;個人認為有以下幾點&am…

vc c語言坐標圖,VC++6.0下C語言畫圖編程問題

復制內容到剪貼板代碼:#include#includevoid CSinusoidView::OnDraw(CDC* pDC){CSinusoidDoc* pDoc GetDocument();ASSERT_VALID(pDoc);// TODO: add draw code for native data here//建立畫筆CPen cpen,pen;pen.CreatePen(PS_SOLID,4,RGB(0,0,0));cpen.CreatePen(PS_SOLID,2…

Java BigDecimal詳解

1.引言 float和double類型的主要設計目標是為了科學計算和工程計算。他們執行二進制浮點運算&#xff0c;這是為了在廣域數值范圍上提供較為精確的快速近似計算而精心設計的。然而&#xff0c;它們沒有提供完全精確的結果&#xff0c;所以不應該被用于要求精確結果的場合。但是…

Erlang庫 -- 有意思的庫匯總

抄自這里 首先&#xff0c;庫存在的目的大致可分為&#xff1a;1、提供便利2、盡可能解決一些痛點首先&#xff0c;我們先明確一下Erlang編程語言的一些痛點&#xff08;偽痛點&#xff09;&#xff1a;1&#xff0c;單進程問題Erlang虛擬機屬于搶占式調度&#xff0c;搶占式調…

windows 串口編程 c語言,windows下C語言版串口發送程序(基于VS2017)

#include "tchar.h"#include int main(){/*****************************打開串口*************************************/HANDLE hCom;//全局變量&#xff0c;串口句柄hCom CreateFile(_T("COM3"),//COM3口GENERIC_READ | GENERIC_WRITE,//允許讀和寫0,/…

scikit-learn決策樹算法類庫使用小結

之前對決策樹的算法原理做了總結&#xff0c;包括決策樹算法原理(上)和決策樹算法原理(下)。今天就從實踐的角度來介紹決策樹算法&#xff0c;主要是講解使用scikit-learn來跑決策樹算法&#xff0c;結果的可視化以及一些參數調參的關鍵點。 1. scikit-learn決策樹算法類庫介紹…

3.js模式-策略模式

1. 策略模式 策略模式定義一系列的算法&#xff0c;把它們封裝起來&#xff0c;并且可以互相替換。 var strategies { isNonEmpty: function(value,errMsg){ if(value ){ return errMsg; } }, minLength:function(value,length,errMsg){ if(value.length < length){ retur…