Apollo源码分析:路径规划 (v5.5)
Apollo開源代碼鏈接:https://github.com/ApolloAuto/apollo
本文主要講解Apollo/modules/planning中的路徑規(guī)劃的架構(gòu)和算法。
第一章? ? 架構(gòu)設(shè)計(jì)與實(shí)現(xiàn)
一、架構(gòu)設(shè)計(jì)與實(shí)現(xiàn)
?
引自:https://github.com/ApolloAuto/apollo/blob/master/modules/planning/README_cn.md
?
Apollo FSM(finite state machine):一個(gè)有限狀態(tài)機(jī),與高清地圖確定車輛狀態(tài)給定其位置和路線。
----------------------------------------------------------------------------------------------------------------------------------------
Planning模塊整體框架:
1. 可執(zhí)行程序?qū)?#xff1a; Planning Dispatcher根據(jù)車輛的狀態(tài)和其他相關(guān)信息,調(diào)用合適的Planner。Planner實(shí)現(xiàn)獲取所需的上下文數(shù)據(jù)和其他信息,確定相應(yīng)的車輛意圖,執(zhí)行該意圖所需的規(guī)劃任務(wù)并生成規(guī)劃軌跡。它還將更新未來作業(yè)的上下文。根據(jù)工作模式不同將規(guī)劃任務(wù)分成不同的Planner模式(LatticePlanner\NaviPlanner\PublicRoadPlanner\RTKReplayPlanner)。
2.app層:每種Planner分成多個(gè)場景Scenario(BareIntersectionUnprotectedScenario、EmergencyPullOverScenario、EmergencyStopScenario、LaneFollowScenario、TestLearningModelScenario、NarrowStreetUTurnScenario、PullOverScenario、ValetParkingScenario、ParkAndGoScenario、StopSignUnprotectedScenario、TrafficLightProtectedScenario、TrafficLightUnprotectedLeftTurnScenario、TrafficLightUnprotectedRightTurnScenario、YieldSignScenario)-》每個(gè)場景又分解成多個(gè)Stage(例如ValetParkingScenario包含StageApproachingParkingSpot和StageParking兩個(gè)Stage)
3.lib層:每個(gè)Stage又分解成可執(zhí)行的Task(包括不同的Deciders & Optimizers庫),Task是Deciders & Optimizers :一組實(shí)現(xiàn)決策任務(wù)和各種優(yōu)化的無狀態(tài)庫。優(yōu)化器特別優(yōu)化車輛的軌跡和速度。決策者是基于規(guī)則的分類決策者,他們建議何時(shí)換車道、何時(shí)停車、何時(shí)爬行(慢速行進(jìn))或爬行何時(shí)完成。
-------------------------------------------------------------------------------------------------------------------------------------------
Planning Context: 作業(yè)的上下文。
這種 可執(zhí)行程序?qū)?》app層 -》lib層 的三層分級(jí)架構(gòu)實(shí)質(zhì)與視覺感知模塊類似。
本文路徑規(guī)劃模塊架構(gòu)與視覺感知模塊架構(gòu)( https://blog.csdn.net/Cxiazaiyu/article/details/106256330 )區(qū)別:
?
視覺感知模塊中更像是自下而上地設(shè)計(jì)出這種三層結(jié)構(gòu)的,因此,我們也自下而上地講解了這個(gè)結(jié)構(gòu);
路徑規(guī)劃模塊更像是自上而下設(shè)計(jì)出這種架構(gòu)的,因此,我們也自上而下地講解這個(gè)結(jié)構(gòu)。
?
視覺感知模塊命名更直白。
路徑規(guī)劃模塊把接口分散放入各個(gè)層的模塊中了,沒有集中放在一個(gè)文件夾。
?
?1. 可執(zhí)行程序?qū)?/strong>--planning/planner
定義抽象基類PlannerDispatcher,包含了Planner類型的數(shù)據(jù)成員;
class PlannerDispatcher {public:virtual std::unique_ptr<Planner> DispatchPlanner() = 0;protected:common::util::Factory<PlannerType, Planner> planner_factory_; };NaviPlannerDispatcher和OnLanePlannerDispatcher繼承自PlannerDispatcher,覆蓋了抽象基類中的virtual std::unique_ptr<Planner> DispatchPlanner()方法,實(shí)現(xiàn)通過工廠模式創(chuàng)建配置文件中指定的Planner對(duì)象。
以?NaviPlannerDispatcher為例:
std::unique_ptr<Planner> NaviPlannerDispatcher::DispatchPlanner() {PlanningConfig planning_config;if (!apollo::cyber::common::GetProtoFromFile(FLAGS_planning_config_file,&planning_config)) {return nullptr;}auto planner_type = PlannerType::NAVI;if (planning_config.has_navigation_planning_config()) {planner_type = planning_config.navigation_planning_config().planner_type(0);}return planner_factory_.CreateObject(planner_type); }Planner為定義的基類接口,Planner中包含了scenario。
class Planner { protected:scenario::ScenarioManager scenario_manager_;scenario::Scenario* scenario_ = nullptr; };?? PlannerWithReferenceLine繼承自 Planner,做了一層抽象:
class PlannerWithReferenceLine : public Planner {};根據(jù)不同的工作模式定義了4種Planner:
class LatticePlanner : public PlannerWithReferenceLine {};/*** @class NaviPlanner* @brief NaviPlanner is a planner based on real-time relative maps. It uses the* vehicle's FLU (Front-Left-Up) coordinate system to accomplish tasks such as* cruising, following, overtaking, nudging, changing lanes and stopping.* Note that NaviPlanner is only used in navigation mode (turn on navigation* mode by setting "FLAGS_use_navigation_mode" to "true") and do not use it in* standard mode.*/ class NaviPlanner : public PlannerWithReferenceLine {};/*** @class PublicRoadPlanner* @brief PublicRoadPlanner is an expectation maximization planner.*/class PublicRoadPlanner : public PlannerWithReferenceLine{};/*** @class RTKReplayPlanner* @brief RTKReplayPlanner is a derived class of Planner.* It reads a recorded trajectory from a trajectory file and* outputs proper segment of the trajectory according to vehicle* position.*/ class RTKReplayPlanner : public PlannerWithReferenceLine{};2. app層--planning/scenarios
前面講到Planner中包含scenario::ScenarioManager scenario_manager_和scenario::Scenario* scenario_ = nullptr數(shù)據(jù)成員,
class Scenario {public:/*** Each scenario should define its own stages object's creation* scenario will call stage's Stage::Process function following a configured* order, The return value of Stage::Process function determines the* transition from one stage to another.*/virtual std::unique_ptr<Stage> CreateStage(const ScenarioConfig::StageConfig& stage_config) = 0;// Each scenario should define its own transfer condition, i.e., when it// should allow to transfer from other scenario to itself.virtual bool IsTransferable(const Scenario& other_scenario,const Frame& frame) {return true;}protected:std::unique_ptr<Stage> current_stage_; };基于scenario派生出BareIntersectionUnprotectedScenario、EmergencyPullOverScenario、EmergencyStopScenario、LaneFollowScenario、TestLearningModelScenario、NarrowStreetUTurnScenario、PullOverScenario、ValetParkingScenario、ParkAndGoScenario、StopSignUnprotectedScenario、TrafficLightProtectedScenario、TrafficLightUnprotectedLeftTurnScenario、TrafficLightUnprotectedRightTurnScenario、YieldSignScenario;
?Scenario類型中又包含Stage類型的數(shù)據(jù)成員。一個(gè)Scenario分解成多個(gè)Stage,不同Stage可能是基于不同規(guī)劃算法實(shí)現(xiàn)的。例如:泊車場景中(Apollo/modules/planning/scenarios/park/valet_parking),劃分成了StageApproachingParkingSpot和StageParking兩個(gè)Stage。
?
Stage中包含Task類型的數(shù)據(jù)成員,依次執(zhí)行Tasklilst上的Task,相當(dāng)于將多個(gè)lib串聯(lián)起來實(shí)現(xiàn)一定功能的app:
class Stage {public:/*** @brief Each stage does its business logic inside Process function.* If the stage want to transit to a different stage after finish,* it should set the type of 'next_stage_'.*/virtual StageStatus Process(const common::TrajectoryPoint& planning_init_point, Frame* frame) = 0;/*** @brief The sequence of tasks inside the stage. These tasks usually will be* executed in order.*/const std::vector<Task*>& TaskList() const { return task_list_; }protected:std::map<TaskConfig::TaskType, std::unique_ptr<Task>> tasks_;std::vector<Task*> task_list_; };3. lib層--planning/tasks
決策任務(wù)deciders和各種優(yōu)化optimizers的無狀態(tài)庫,都是基于Task基類派生。
決策者是基于規(guī)則的分類決策者,他們建議何時(shí)換車道、何時(shí)停車、何時(shí)爬行(慢速行進(jìn))或爬行何時(shí)完成。
class Decider : public Task {};Decider又派生出:CreepDecider、LaneChangeDecider、OpenSpaceFallbackDecider、OpenSpacePreStopDecider、OpenSpaceRoiDecider、PathAssessmentDecider、PathBoundsDecider、PathDecider、PathLaneBorrowDecider、PathReuseDecider、RssDecider、RuleBasedStopDecider、SpeedBoundsDecider、SpeedLimitDecider、SpeedDecider、STBoundsDecider
優(yōu)化器特別優(yōu)化車輛的軌跡和速度。
class PathOptimizer : public Task {}; class SpeedOptimizer : public Task {}; class TrajectoryOptimizer : public Task{};具體包括:
class OpenSpaceTrajectoryProvider : public TrajectoryOptimizer{}; class OpenSpaceTrajectoryPartition : public TrajectoryOptimizer{}; class PathTimeHeuristicOptimizer : public SpeedOptimizer {}; class PiecewiseJerkPathOptimizer : public PathOptimizer{}; class PiecewiseJerkSpeedNonlinearOptimizer : public SpeedOptimizer{}; class PiecewiseJerkSpeedOptimizer : public SpeedOptimizer{};關(guān)于Apollo路徑規(guī)劃更詳細(xì)的資料可參考:https://blog.csdn.net/davidhopper/article/details/79176505?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.control
?
第二章??? 設(shè)計(jì)模式
一、簡單工廠模式
Apollo/modules/planning/scenarios/scenario_manager.cc
std::unique_ptr<Scenario> ScenarioManager::CreateScenario(ScenarioConfig::ScenarioType scenario_type) {std::unique_ptr<Scenario> ptr;switch (scenario_type) {case ScenarioConfig::BARE_INTERSECTION_UNPROTECTED:ptr.reset(new scenario::bare_intersection::BareIntersectionUnprotectedScenario(config_map_[scenario_type], &scenario_context_));break;case ScenarioConfig::EMERGENCY_PULL_OVER:ptr.reset(new emergency_pull_over::EmergencyPullOverScenario(config_map_[scenario_type], &scenario_context_));break;case ScenarioConfig::EMERGENCY_STOP:ptr.reset(new emergency_stop::EmergencyStopScenario(config_map_[scenario_type], &scenario_context_));break;case ScenarioConfig::LANE_FOLLOW:ptr.reset(new lane_follow::LaneFollowScenario(config_map_[scenario_type],&scenario_context_));break;case ScenarioConfig::PARK_AND_GO:ptr.reset(new scenario::park_and_go::ParkAndGoScenario(config_map_[scenario_type], &scenario_context_));break;case ScenarioConfig::PULL_OVER:ptr.reset(new scenario::pull_over::PullOverScenario(config_map_[scenario_type], &scenario_context_));break;case ScenarioConfig::STOP_SIGN_UNPROTECTED:ptr.reset(new scenario::stop_sign::StopSignUnprotectedScenario(config_map_[scenario_type], &scenario_context_));break;case ScenarioConfig::TEST_LEARNING_MODEL:ptr.reset(new scenario::TestLearningModelScenario(config_map_[scenario_type], &scenario_context_));break;case ScenarioConfig::TRAFFIC_LIGHT_PROTECTED:ptr.reset(new scenario::traffic_light::TrafficLightProtectedScenario(config_map_[scenario_type], &scenario_context_));break;case ScenarioConfig::TRAFFIC_LIGHT_UNPROTECTED_LEFT_TURN:ptr.reset(new scenario::traffic_light::TrafficLightUnprotectedLeftTurnScenario(config_map_[scenario_type], &scenario_context_));break;case ScenarioConfig::TRAFFIC_LIGHT_UNPROTECTED_RIGHT_TURN:ptr.reset(new scenario::traffic_light::TrafficLightUnprotectedRightTurnScenario(config_map_[scenario_type], &scenario_context_));break;case ScenarioConfig::VALET_PARKING:ptr.reset(new scenario::valet_parking::ValetParkingScenario(config_map_[scenario_type], &scenario_context_));break;case ScenarioConfig::YIELD_SIGN:ptr.reset(new scenario::yield_sign::YieldSignScenario(config_map_[scenario_type], &scenario_context_));break;default:return nullptr;}if (ptr != nullptr) {ptr->Init();}return ptr;與視覺感知模塊(https://blog.csdn.net/Cxiazaiyu/article/details/106256330)中使用的簡單工廠模式類似,這里不再贅述。
二、工廠方法模式(基于類模板)
工廠方法模式:調(diào)用抽象工廠的接口,返回抽象產(chǎn)品的指針;根據(jù)輸入配置參數(shù)不同,在抽象工廠接口內(nèi)部調(diào)用相應(yīng)的具體工廠,生產(chǎn)相應(yīng)的具體產(chǎn)品,并將具體產(chǎn)品的指針綁定在抽象產(chǎn)品的指針上作為返回值。Register解決的就是實(shí)現(xiàn)“根據(jù)輸入配置參數(shù)不同,在抽象工廠接口內(nèi)部調(diào)用相應(yīng)的具體工廠”,方法是建立一個(gè)map,根據(jù)配置參數(shù)名稱映射到相應(yīng)的具體工廠。
路徑規(guī)劃模塊(Apollo/modules/planning)主要使用基于類模板實(shí)現(xiàn)工廠方法模式創(chuàng)建對(duì)象。包括在planner_dispatcher中創(chuàng)建planner對(duì)象(?Apollo/modules/planning/planner/planner_dispatcher.h中定義 common::util::Factory<PlannerType, Planner> planner_factory_;),在scenarios中創(chuàng)建stage對(duì)象(Apollo/modules/planning/scenarios/stop_sign/unprotected/stop_sign_unprotected_scenario.cc中定義的? static apollo::common::util::Factory<ScenarioConfig::StageType, Stage, Stage* (*)(const ScenarioConfig::StageConfig& stage_config)> s_stage_factory_;),在stage中創(chuàng)建task(Apollo/modules/planning/scenarios/stage.cc中調(diào)用auto ptr = TaskFactory::CreateTask(*config_map[task_type]);)。
2.1 UML結(jié)構(gòu)
參與者如下(這里以task_factory_一條應(yīng)用鏈路為例,planner_factory_的鏈路與之類似):
| 參與者 | 作用 | 在Apollo中的示例 |
| 抽象工廠 | 類模板,創(chuàng)建對(duì)象調(diào)用的接口和Register的接口 | 由Factory(factory.h中定義)實(shí)例化出的task_factory_ |
| 具體工廠 | 實(shí)例化抽象工廠的類模板,負(fù)責(zé)生產(chǎn)具體產(chǎn)品。將抽象工廠的指針綁定在具體工廠指針上,根據(jù)需要調(diào)用相應(yīng)的具體工廠以生產(chǎn)相應(yīng)的具體產(chǎn)品 | task_factory_中輸入的creator(這里是匿名函數(shù)的形式) |
| 抽象產(chǎn)品 | 抽象工廠返回類型為抽象產(chǎn)品的指針 | Task |
| 具體產(chǎn)品 | 綁定在基類(抽象產(chǎn)品)指針上的具體派生類指針 | LaneChangeDecider、SpeedBoundsDecider、OpenSpaceRoiDecider (Decider和PathOptimizer是根據(jù)邏輯需要在抽象產(chǎn)品和具體產(chǎn)品之間抽象的一層) |
注意:相比于一般只能創(chuàng)建一種抽象產(chǎn)品的工廠方法模式,這里設(shè)計(jì)的工廠方法模式還支持創(chuàng)建不同的抽象產(chǎn)品(基于類模板,把抽象產(chǎn)品類型作為參數(shù))。
?
2.2 實(shí)現(xiàn)方法
2.2.1 抽象工廠 & 抽象產(chǎn)品 、 map & 創(chuàng)建對(duì)象的接口
- 類模板的定義
Apollo/modules/common/util/factory.h 中Factory類模板提供了注冊(cè)和創(chuàng)建對(duì)象的接口。
template <typename IdentifierType, class AbstractProduct, //IdentifierType為派生類名的類型(一般可設(shè)為string),AbstractProduct為基類class ProductCreator = AbstractProduct *(*)(),class MapContainer = std::map<IdentifierType, ProductCreator>> class Factory {public:/*** @brief Registers the class given by the creator function, linking it to id.* Registration must happen prior to calling CreateObject.* @param id Identifier of the class being registered* @param creator Function returning a pointer to an instance of* the registered class* @return True if the key id is still available*/bool Register(const IdentifierType &id, ProductCreator creator) { //注冊(cè)的接口return producers_.insert(std::make_pair(id, creator)).second;}/*** @brief Creates and transfers membership of an object of type matching id.* Need to register id before CreateObject is called. May return nullptr* silently.* @param id The identifier of the class we which to instantiate* @param args the object construction arguments*/template <typename... Args>std::unique_ptr<AbstractProduct> CreateObjectOrNull(const IdentifierType &id, //創(chuàng)建對(duì)象的接口,返回的是抽象產(chǎn)品(基類)的指針Args &&... args) { auto id_iter = producers_.find(id);if (id_iter != producers_.end()) {return std::unique_ptr<AbstractProduct>((id_iter->second)(std::forward<Args>(args)...));}return nullptr;}/*** @brief Creates and transfers membership of an object of type matching id.* Need to register id before CreateObject is called.* @param id The identifier of the class we which to instantiate* @param args the object construction arguments*/template <typename... Args>std::unique_ptr<AbstractProduct> CreateObject(const IdentifierType &id, //創(chuàng)建對(duì)象的接口Args &&... args) {auto result = CreateObjectOrNull(id, std::forward<Args>(args)...);AERROR_IF(!result) << "Factory could not create Object of type : " << id;return result;}private:MapContainer producers_; };- map定義:
std::map<IdentifierType, ProductCreator>> producers_;
注冊(cè)就是將實(shí)際需要生產(chǎn)的具體產(chǎn)品的名稱和具體工廠插入producers_,在需要?jiǎng)?chuàng)建對(duì)象時(shí)根據(jù)具體產(chǎn)品的名稱索引到具體工廠,調(diào)用具體工廠生產(chǎn)具體產(chǎn)品,并將具體產(chǎn)品的指針綁定在抽象產(chǎn)品的指針上作為具體工廠的返回值。
?
- 基于抽象工廠類模板+抽象產(chǎn)品作為模板實(shí)參=》實(shí)例化出抽象工廠
在Apollo/modules/planning/tasks/task_factory.h中針對(duì)task基類對(duì)工廠模板類進(jìn)行了實(shí)例化,產(chǎn)生task_factory_抽象工廠,并使用TaskFactory類對(duì)task_factory_抽象工廠做了一層封裝。
class TaskFactory {public:...private:static apollo::common::util::Factory<TaskConfig::TaskType, Task, Task *(*)(const TaskConfig &config),std::unordered_map<TaskConfig::TaskType, Task *(*)(const TaskConfig &config), std::hash<int>>> task_factory_;static std::unordered_map<TaskConfig::TaskType, TaskConfig, std::hash<int>>default_task_configs_; };上文已經(jīng)講過Task為基類(抽象產(chǎn)品),派生出各種Decider和Optimizer的具體產(chǎn)品。
2.2.2 具體工廠 & 具體產(chǎn)品、 Register(向map中添加元素)
//Register void TaskFactory::Init(const PlanningConfig& config) {task_factory_.Register(TaskConfig::LANE_CHANGE_DECIDER,[](const TaskConfig& config) -> Task* {return new LaneChangeDecider(config); //使用匿名函數(shù)定義具體工廠,具體工廠生產(chǎn)具體產(chǎn)品});task_factory_.Register(TaskConfig::SPEED_BOUNDS_PRIORI_DECIDER,[](const TaskConfig& config) -> Task* {return new SpeedBoundsDecider(config);});task_factory_.Register(TaskConfig::OPEN_SPACE_ROI_DECIDER,[](const TaskConfig& config) -> Task* {return new OpenSpaceRoiDecider(config);}); }std::unique_ptr<Task> TaskFactory::CreateTask(const TaskConfig& task_config) {TaskConfig merged_config;if (default_task_configs_.find(task_config.task_type()) !=default_task_configs_.end()) {merged_config = default_task_configs_[task_config.task_type()];}merged_config.MergeFrom(task_config);return task_factory_.CreateObject(task_config.task_type(), merged_config); }特點(diǎn):在Apollo/modules/planning/tasks/task_factory.cc定義中對(duì)task的派生類LaneChangeDecider、PATH_LANE_BORROW_DECIDER等集中進(jìn)行注冊(cè)。
2.2.3 Client使用示例:
?Apollo/modules/planning/scenarios/stage.cc中讀入配置參數(shù),調(diào)用抽象工廠的接口,依次生產(chǎn)具體產(chǎn)品:
for (int i = 0; i < config_.task_type_size(); ++i) {auto task_type = config_.task_type(i);ACHECK(config_map.find(task_type) != config_map.end())<< "Task: " << TaskConfig::TaskType_Name(task_type)<< " used but not configured";auto iter = tasks_.find(task_type);if (iter == tasks_.end()) {auto ptr = TaskFactory::CreateTask(*config_map[task_type]); //根據(jù)配置文件的參數(shù)動(dòng)態(tài)地創(chuàng)建具體產(chǎn)品task_list_.push_back(ptr.get());tasks_[task_type] = std::move(ptr);} else {task_list_.push_back(iter->second.get());}}可參考:https://blog.csdn.net/davidhopper/article/details/79197075
?
下表總結(jié)對(duì)比基于類模板實(shí)現(xiàn)工廠方法模式和視覺感知模塊(https://blog.csdn.net/Cxiazaiyu/article/details/106256330)使用的基于宏定義的工廠方法模式的異同:
| ? | 基于類模板的工廠方法實(shí)現(xiàn) | 基于宏定義的工廠方法實(shí)現(xiàn) |
| 可適用不同基類類型的抽象工廠機(jī)理 | 工廠的類模板只有一個(gè),利用類模板機(jī)理,將抽象產(chǎn)品類型(基類)作為模板參數(shù),根據(jù)抽象產(chǎn)品類型實(shí)例化出不同的抽象工廠 | 只有一個(gè)抽象工廠原型類,使用返回創(chuàng)建對(duì)象的接口使用Any類型機(jī)理(支持代表任意類型)保證可以返回任意抽象產(chǎn)品 |
| 實(shí)現(xiàn)方法的區(qū)別 | 直接將Register和CreateObject作為工廠類模板的兩個(gè)方法,使用類模板Factory實(shí)例化出的對(duì)象調(diào)用Register和CreateObject方法 ? ? ? | CreateObject: 為每一個(gè)基類宏展開一個(gè)伴隨的Register類(實(shí)際作用是CreateObject),該Register類中給出創(chuàng)建對(duì)象的接口; Register: 為每一個(gè)派生類宏展開一個(gè)伴隨的Register函數(shù),該Register函數(shù)建立配置參數(shù)名稱到具體工廠的map。 ? |
| Register的map的區(qū)別 (相同點(diǎn):都基于Register添加一個(gè)map,建立根據(jù)輸入配置文件參數(shù)映射到對(duì)應(yīng)的具體工廠) 無論是基于宏定義還是類模板,在抽象工廠和具體工廠之間需要實(shí)現(xiàn)怎么基于抽象工廠的接口調(diào)用具體工廠(Register機(jī)理建立map后,使用map索引到真實(shí)綁定的具體工廠)。? | 每一個(gè)基類維護(hù)一個(gè)Local的map (std::map<IdentifierType, ProductCreator>> producers_;),通過producers_[派生類名]可以調(diào)用對(duì)應(yīng)的具體工廠; 不同基類維護(hù)不同的Local map。 | 維護(hù)一個(gè)Global的map, (static std::map<string, std::map<std::string, ObjectFactory *>> factory_map;),通過factory_map[基類名][派生類名]可以調(diào)用對(duì)應(yīng)的具體工廠 |
| 具體工廠的實(shí)現(xiàn)機(jī)制 | 直接使用匿名函數(shù) | 在派生類中使用宏定義展開成對(duì)應(yīng)的具體工廠類,繼承自抽象工廠類 |
| Register的方式區(qū)別 | 在factory的Register方法中集中Register,將派生類名-具體工廠的對(duì)應(yīng)關(guān)系插入到map中; ? | 在每個(gè)派生類定義中分布式地Register,將[基類名][派生類名]-具體工廠的對(duì)應(yīng)關(guān)系插入到map中; ? |
| 優(yōu)\缺點(diǎn) | 優(yōu)點(diǎn):類模板工廠定義簡單 缺點(diǎn):如果需要添加新的基類,需要重新實(shí)例化factory;如果需要添加新的派生類,需要在實(shí)例化的factory中集中Register。 | 缺點(diǎn):定義相比類模板實(shí)現(xiàn)略顯復(fù)雜 優(yōu)點(diǎn):如果需要增加新的基類和派生類,不需要修改原來的工廠相關(guān)的代碼,僅需要在新添加的基類和派生類后調(diào)用宏定義。 |
?
總結(jié)
以上是生活随笔為你收集整理的Apollo源码分析:路径规划 (v5.5)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: php owncloud_技术|搭建私有
- 下一篇: 接口请求返回的状态码总览