java多态替换switch_第1章 重构,第一个案例(3):运用多态取代switch
3.運用多態取代與價格相關的條件邏輯
3.1 switch和“常客積分”代碼的再次搬遷
(1)switch:最好不要在另一個對象的屬性上運用switch語句
switch(getMovie().getPriceCode()) //在movie對象的priceCode屬性上運用switch
{ //這意味著可以將getCharge函數從Rental類移動到Movie類去//選擇在Movie類中封裝計算費用功能,還有一個//原因,就是可以控制因影片類型變化導致的計算//方式變化,從而對其它對象產生影響。
…
}
(2)常客積分:getFrequentRenterPoints函數的再次搬遷。用跟處理getCharge相同的手法處理常客積分,將因影片類型變化而變化的所有東西都放到Movie類中去處理。Rental類中只需調用Movie相應的方法即可。
【實例分析】影片出租1.3.1
//第1章:重構,第1個案例//場景:影片出租,計算每一位顧客的消費金額/*說明:
1. 影片分3類:普通片、兒童片和新片。
2. 每種影片計算租金的方式。
A.普通片:基本租金為2元,超過2天的部分每天加1.5元
B.新片:租期*3
C.兒童片:基本租金為1.5元,超過3天的部分每天加1.5元
3. 積分的計算:每借1片,積分加1,如果是新片且租期1天以上的額外贈送1分。*/#include#include#include#include
using namespacestd;//影片類
classMovie
{private:string title; //片名
int priceCode; //價格
public:enumMovieType{
REGULAR= 0, //普通片
NEW_RELEASE, //新片
CHILDRENS //兒童片
};
Movie(string title, intpriceCode)
{this->title =title;this->priceCode =priceCode;
}string getTitle(){returntitle;}void setTitle(stringvalue)
{
title=value;
}int getPriceCode(){returnpriceCode;}void setPriceCode(intvalue)
{this->priceCode =value;
}//將原來Rental類中的getCharge移到該類中,并將租期作為參數傳入//搬到這里來的原因是//1.switch語句中getPriceCode為本類對象的屬性//2.封裝影片類型的變化導致計算方式變化于該類中,從而降低對其他類的影響
double getCharge(intdaysRented)
{double result = 0 ;//相當于statement中的thisamount;
switch(getPriceCode())
{caseMovie::REGULAR:
result+= 2; //普通片基本租金為2元
if(daysRented > 2) //超過2天的每天加1.5元
result +=(daysRented - 2 ) * 1.5;break;caseMovie::NEW_RELEASE:
result+= daysRented * 3; //新片的租金
break;caseMovie::CHILDRENS:
result+= 1.5; //兒童片基本租金為1.5元
if(daysRented > 3) //超過3天的每天加1.5元
result +=(daysRented - 3 ) * 1.5;break;
}returnresult;
}//將原Rental類中常客積分搬到該類中//原因是常客積分的計費方式與影片類型有關,也是為了控制當//影片類型變化時,由于計算方式變化對其他類的影響
int getFrequentRenterPoints(intdaysRented)
{//如果是新片且租期超過1天以上,則額外送1分積分
if ((getPriceCode() == Movie::NEW_RELEASE) &&daysRented> 1 ) return 2;else return 1;
}
};//租賃類(表示某個顧客租了一部影片)
classRental
{private:
Movie& movie; //所租的影片
int daysRented; //租期
public:
Rental(Movie& movie, intdaysRented):movie(movie)
{this->daysRented =daysRented;
}int getDaysRented(){returndaysRented;}
Movie&getMovie()
{returnmovie;
}doublegetCharge()
{returnmovie.getCharge(daysRented);
}//將原Customer類的statement中計算常客積分的代碼移到Rental類
intgetFrequentRenterPoints()
{returnmovie.getFrequentRenterPoints(daysRented);
}
};//顧客類(用來表示顧客)
classCustomer
{private:string name; //顧客姓名
vector rentals; //每個租賃記錄//獲得總消費
doublegetTotalCharge()
{double result = 0;
vector::iterator iter =rentals.begin();while( iter !=rentals.end())
{
Rental& each = *(*iter);
result+=each.getCharge();++iter;
}returnresult;
}//獲得總積分
intgetTotalFrequentRenterPointers()
{int result = 0;
vector::iterator iter =rentals.begin();while( iter !=rentals.end())
{
Rental& each = *(*iter);
result+=each.getFrequentRenterPoints();++iter;
}returnresult;
}voidcleanUp()
{
vector::iterator iter =rentals.begin();while( iter !=rentals.end())
{delete(*iter);++iter;
}
rentals.clear();
}
template
stringnumToString(T num)
{
stringstream ss;
ss<
}public:
Customer(stringname)
{this->name =name;
}void addRental(Rental*value)
{
rentals.push_back(value);
}string getName(){returnname;}//statement(報表),生成租賃的詳單
stringstatement()
{string ret = "Rental Record for" + name + "\n";
vector::iterator iter =rentals.begin();while( iter !=rentals.end())
{
Rental& each = *(*iter);//顯示每個租賃記錄
ret+= "\t" + each.getMovie().getTitle() + "\t" +numToString(each.getCharge())+ "\n";++iter;
}//增加頁腳注釋
ret += "Amount owed is" + numToString(getTotalCharge()) + "\n";//用getTotalCharge代替totalAmount
ret += "You earned" + numToString(getTotalFrequentRenterPointers()) +"\n";returnret;
}~Customer()
{
cleanUp();
}
};void init(Customer&customer)
{
Movie* mv = new Movie("倚天屠龍記",Movie::REGULAR);
Rental* rt = new Rental(*mv, 2);
customer.addRental(rt);
mv= new Movie("新水滸傳",Movie::NEW_RELEASE);
rt= new Rental(*mv, 3);
customer.addRental(rt);
mv= new Movie("喜羊羊與灰太狼",Movie::CHILDRENS);
rt= new Rental(*mv, 5);
customer.addRental(rt);
}intmain()
{
Customer customer("SantaClaus");
init(customer);
cout<< customer.statement() <
}/*輸出結果
Rental Record for SantaClaus
倚天屠龍記 2
新水滸傳 9
喜羊羊與灰太狼 4.5
Amount owed is 15.5
You earned 4*/
View Code
3.2運用子類取代類型碼
(1)使用繼承子類的方式,可以利用多態來取代switch語句。
(2)重構的步驟
①使用“自我封裝字段”的方法將類型碼通過get/set函數封裝起來(如Movie類的getPriceCode函數)。如果類型碼被傳遞給構造函數,就需要將構造函數換成工廠方法(如createMovie函數)
②以類型碼的宿主類為基類,為類型碼的每一個數值建立一個相應的子類。在每個子類中覆寫類型碼的取值函數,使其返回相應的類型碼值。(見Movie子類getPriceCode)
③從父類中刪除保存類型碼的字段(即舊Movie類的priceCode字段),將類型碼訪問函數聲明為抽象函數(如Movie中的getPriceCode)
④使用pushDownMethod/Field方法將與特定類型碼相關的函數推到子類來實現(如本例中的getCharge函數)
(3)缺點:
①對于某個具體對象,在其生命周期中其狀態(或本例中類型碼)是不可改變的(如代表Movie子類型的priceCode是不能更改的),所以當創建了一部影片出來以后,就不能修改其類型了。如,現在某影片是“新片”類型,但即使隨著時間的推移,也不能更改為“普通片”或“兒童片”了。
②我們總是在避免使用switch語句,雖然利用了多態將各個case語句的代碼分解到相應的子類中去實現。但在Movie類的createMovie函數中仍然要出現switch語句。幸運的是,僅此一處用到switch,并且只用于決定創建何種對象而沒有其他的業務邏輯,所以這樣的switch語句是可以接受的。
【實例分析】影片出租1.3.2
//types.h
#include #include#include
using namespacestd;//影片類
classMovie
{private:string title; //片名//int priceCode;//價格,注意,這里被注釋了
public:enumMovieType{
REGULAR= 0, //普通片
NEW_RELEASE, //新片
CHILDRENS //兒童片
};
Movie(stringtitle);static Movie* createMovie(string title, intpriceCode);stringgetTitle();void setTitle(stringvalue);//類型碼的“自我封裝”(提供取值函數)
virtual int getPriceCode() = 0;//將原來Rental類中的getCharge移到該類中,并將租期作為參數傳入//搬到這里來的原因是//1.switch語句中getPriceCode為本類對象的屬性//2.封裝影片類型的變化導致計算方式變化于該類中,從而降低對其他類的影響
virtual double getCharge(intdaysRented);int getFrequentRenterPoints(intdaysRented);
};//普通片:用子類取代類型碼
class RegularMovie: publicMovie
{public:
RegularMovie(stringtitle);intgetPriceCode();double getCharge(intdaysRented);
};//兒童片:
class ChildrensMovie: publicMovie
{public:
ChildrensMovie(stringtitle);intgetPriceCode();double getCharge(intdaysRented);
};//新片
class ReleaseMovie: publicMovie
{public:
ReleaseMovie(stringtitle);intgetPriceCode();double getCharge(intdaysRented);
};//租賃類(表示某個顧客租了一部影片)
classRental
{private:
Movie& movie; //所租的影片
int daysRented; //租期
public:
Rental(Movie& movie, intdaysRented);intgetDaysRented();
Movie&getMovie();doublegetCharge();//將原Customer類的statement中計算常客積分的代碼移到Rental類
intgetFrequentRenterPoints();
};//顧客類(用來表示顧客)
classCustomer
{private:string name; //顧客姓名
vector rentals; //每個租賃記錄//獲得總消費
doublegetTotalCharge();//獲得總積分
intgetTotalFrequentRenterPointers();voidcleanUp();
template
stringnumToString(T num)
{
stringstream ss;
ss<
}public:
Customer(stringname);void addRental(Rental*value);stringgetName();//statement(報表),生成租賃的詳單
stringstatement();~Customer();
};
//main.cpp
//第1章:重構,第1個案例//場景:影片出租,計算每一位顧客的消費金額/*說明:
1. 影片分3類:普通片、兒童片和新片。
2. 每種影片計算租金的方式。
A.普通片:基本租金為2元,超過2天的部分每天加1.5元
B.新片:租期*3
C.兒童片:基本租金為1.5元,超過3天的部分每天加1.5元
3. 積分的計算:每借1片,積分加1,如果是新片且租期1天以上的額外贈送1分。*/#include#include"types.h"
using namespacestd;//*********************************************影片類*************************************
Movie::Movie(stringtitle)
{this->title =title;
}//提供創建子類實例的靜態函數(也可以使用工廠方法)
Movie* Movie::createMovie(string title, intpriceCode)
{
Movie* ret =NULL;//利用子類替代switch的分支。我們總是在避免使用switch語句。但這里//只有一處用到switch,并且只用于決定創建何種對象而沒有其他的業務邏輯//所以這樣的switch語句是可以接受的。
switch(priceCode)
{caseMovie::REGULAR:
ret= newRegularMovie(title);break;caseMovie::CHILDRENS:
ret= newChildrensMovie(title);break;caseMovie::NEW_RELEASE:
ret= newReleaseMovie(title);break;
}returnret;
}stringMovie::getTitle()
{returntitle;
}void Movie::setTitle(stringvalue)
{
title=value;
}double Movie::getCharge(intdaysRented)
{returnMovie::REGULAR;
}//將原Rental類中常客積分搬到該類中//原因是常客積分的計費方式與影片類型有關,也是為了控制當//影片類型變化時,由于計算方式變化對其他類的影響
int Movie::getFrequentRenterPoints(intdaysRented)
{//如果是新片且租期超過1天以上,則額外送1分積分
if ((getPriceCode() == Movie::NEW_RELEASE) &&daysRented> 1 ) return 2;else return 1;
}//普通片:用子類取代類型碼
RegularMovie::RegularMovie(stringtitle):Movie(title)
{
}intRegularMovie::getPriceCode()
{returnMovie::REGULAR;
}//使用pushDownMethod(Field)方法將與特定類型碼相關的函數推到子類來實現
double RegularMovie::getCharge(intdaysRented)
{double result = 2;if(daysRented > 2) //超過2天的每天加1.5元
result +=(daysRented - 2 ) * 1.5;returnresult;
}//兒童片
ChildrensMovie::ChildrensMovie(stringtitle):Movie(title)
{
}intChildrensMovie::getPriceCode()
{returnMovie::CHILDRENS;
}//使用pushDownMethod(Field)方法將與特定類型碼相關的函數推到子類來實現
double ChildrensMovie::getCharge(intdaysRented)
{double result = 1.5;//兒童片基本租金為1.5元
if(daysRented > 3) //超過3天的每天加1.5元
result +=(daysRented - 3 ) * 1.5;returnresult;
}//新片
ReleaseMovie::ReleaseMovie(stringtitle):Movie(title)
{
}intReleaseMovie::getPriceCode()
{returnMovie::NEW_RELEASE;
}//使用pushDownMethod(Field)方法將與特定類型碼相關的函數推到子類來實現
double ReleaseMovie::getCharge(intdaysRented)
{return daysRented * 3; //新片的租金
}//********************************租賃類(表示某個顧客租了一部影片)**********************************
Rental::Rental(Movie& movie, intdaysRented):movie(movie)
{this->daysRented =daysRented;
}int Rental::getDaysRented(){returndaysRented;}
Movie&Rental::getMovie()
{returnmovie;
}doubleRental::getCharge()
{returnmovie.getCharge(daysRented);
}//將原Customer類的statement中計算常客積分的代碼移到Rental類
intRental::getFrequentRenterPoints()
{returnmovie.getFrequentRenterPoints(daysRented);
}//*********************************顧客類(用來表示顧客)*************************************//獲得總消費
doubleCustomer::getTotalCharge()
{double result = 0;
vector::iterator iter =rentals.begin();while( iter !=rentals.end())
{
Rental& each = *(*iter);
result+=each.getCharge();++iter;
}returnresult;
}//獲得總積分
intCustomer::getTotalFrequentRenterPointers()
{int result = 0;
vector::iterator iter =rentals.begin();while( iter !=rentals.end())
{
Rental& each = *(*iter);
result+=each.getFrequentRenterPoints();++iter;
}returnresult;
}voidCustomer::cleanUp()
{
vector::iterator iter =rentals.begin();while( iter !=rentals.end())
{delete(*iter);++iter;
}
rentals.clear();
}
Customer::Customer(stringname)
{this->name =name;
}void Customer::addRental(Rental*value)
{
rentals.push_back(value);
}string Customer::getName(){returnname;}//statement(報表),生成租賃的詳單
stringCustomer::statement()
{string ret = "Rental Record for" + name + "\n";
vector::iterator iter =rentals.begin();while( iter !=rentals.end())
{
Rental& each = *(*iter);//顯示每個租賃記錄
ret+= "\t" + each.getMovie().getTitle() + "\t" +numToString(each.getCharge())+ "\n";++iter;
}//增加頁腳注釋
ret += "Amount owed is" + numToString(getTotalCharge()) + "\n";//用getTotalCharge代替totalAmount
ret += "You earned" + numToString(getTotalFrequentRenterPointers()) +"\n";returnret;
}
Customer::~Customer()
{
cleanUp();
}//************************************************初始化數據********************************************
void init(Customer&customer)
{
Movie* mv = Movie::createMovie("倚天屠龍記",Movie::REGULAR);
Rental* rt = new Rental(*mv, 2);
customer.addRental(rt);
mv= Movie::createMovie("新水滸傳",Movie::NEW_RELEASE);
rt= new Rental(*mv, 3);
customer.addRental(rt);
mv= Movie::createMovie("喜羊羊與灰太狼",Movie::CHILDRENS);
rt= new Rental(*mv, 5);
customer.addRental(rt);
}intmain()
{
Customer customer("SantaClaus");
init(customer);
cout<< customer.statement() <
}/*輸出結果
Rental Record for SantaClaus
倚天屠龍記 2
新水滸傳 9
喜羊羊與灰太狼 4.5
Amount owed is 15.5
You earned 4*/
3.3以state/strategy取代類型碼
(1)對象的狀態在生命周期內可以變化,可以選用state模式(本例中有3種狀態:REGULAR、CHILDREN、NEW_RELEASE)。而前一個例子中,對象與其狀態是緊耦合的,本例利用state模式來組合對象與其狀態,達到松耦合的目的。
(2)重構的步驟
①使用SelfEncapsulate Field來封裝類型碼,確保任何時候都通過取值和設值函數訪問類型代碼。
②新建一個Price類,并在其中提供類型相關的函數。如Price類中加入一個純虛函數getPriceCode,并在所有子類中加上對應的具體函數。
③為Price添加子類,每個子類對應一種類型碼。并提供getPriceCode的具體實現。
④利用pushDownMethod/Field方法將與類型碼相關的函數從Price類推到子類去實現。(如getCharge、getFrequentRenterPoints函數)
⑤在Movie類中保存一個Price的引用,用來保存新建的狀態對象。同時調整Movie中各個與類型碼相關的函數,將動作轉發到狀態對象上(如Movie的getCharge函數)。
(3)引入state模式的好處
①如果要修改任何與價格有關的行為只需修改相應的子類即可。添加新的定價標準也只需擴展新的Price子類即可。
②修改影片分類結構或是改變費用的計算規則、改變常客積分計算規則都很容易。
【實例分析】影片出租1.3.3
//types.h
#include #include#include
using namespacestd;enumMovieType{
REGULAR= 0, //普通片
NEW_RELEASE, //新片
CHILDRENS //兒童片
};//price類
classPrice
{public:virtual int getPriceCode() = 0;virtual double getCharge(int daysRented) = 0;virtual int getFrequentRenterPoints(int daysRented){return 1;}virtual ~Price(){}
};//普通:
class RegularPrice: publicPrice
{public:intgetPriceCode();double getCharge(intdaysRented);
};//兒童片:
class ChildrensPrice: publicPrice
{public:intgetPriceCode();double getCharge(intdaysRented);
};//新片
class ReleasePrice: publicPrice
{public:intgetPriceCode();double getCharge(intdaysRented);int getFrequentRenterPoints(intdaysRented);
};//影片類
classMovie
{private:string title; //片名
Price* price; //價格類
public:
Movie(string title, intpriceCode);~Movie();stringgetTitle();void setTitle(stringvalue);intgetPriceCode();void setPriceCode(intpriceCode);double getCharge(intdaysRented);int getFrequentRenterPoints(intdaysRented);
};//租賃類(表示某個顧客租了一部影片)
classRental
{private:
Movie& movie; //所租的影片
int daysRented; //租期
public:
Rental(Movie& movie, intdaysRented);intgetDaysRented();
Movie&getMovie();doublegetCharge();//將原Customer類的statement中計算常客積分的代碼移到Rental類
intgetFrequentRenterPoints();
};//顧客類(用來表示顧客)
classCustomer
{private:string name; //顧客姓名
vector rentals; //每個租賃記錄//獲得總消費
doublegetTotalCharge();//獲得總積分
intgetTotalFrequentRenterPointers();voidcleanUp();
template
stringnumToString(T num)
{
stringstream ss;
ss<
}public:
Customer(stringname);void addRental(Rental*value);stringgetName();//statement(報表),生成租賃的詳單
stringstatement();~Customer();
};
//main.cpp
//第1章:重構,第1個案例//場景:影片出租,計算每一位顧客的消費金額/*說明:
1. 影片分3類:普通片、兒童片和新片。
2. 每種影片計算租金的方式。
A.普通片:基本租金為2元,超過2天的部分每天加1.5元
B.新片:租期*3
C.兒童片:基本租金為1.5元,超過3天的部分每天加1.5元
3. 積分的計算:每借1片,積分加1,如果是新片且租期1天以上的額外贈送1分。*/#include#include"types.h"
using namespacestd;//*******************************************價格類**************************************//普通片:用子類取代類型碼
intRegularPrice::getPriceCode()
{returnREGULAR;
}//使用pushDownMethod(Field)方法將與特定類型碼相關的函數推到子類來實現
double RegularPrice::getCharge(intdaysRented)
{double result = 2;if(daysRented > 2) //超過2天的每天加1.5元
result +=(daysRented - 2 ) * 1.5;returnresult;
}//兒童片
intChildrensPrice::getPriceCode()
{returnCHILDRENS;
}//使用pushDownMethod(Field)方法將與特定類型碼相關的函數推到子類來實現
double ChildrensPrice::getCharge(intdaysRented)
{double result = 1.5;//兒童片基本租金為1.5元
if(daysRented > 3) //超過3天的每天加1.5元
result +=(daysRented - 3 ) * 1.5;returnresult;
}//新片
intReleasePrice::getPriceCode()
{returnNEW_RELEASE;
}//使用pushDownMethod(Field)方法將與特定類型碼相關的函數推到子類來實現
double ReleasePrice::getCharge(intdaysRented)
{return daysRented * 3; //新片的租金
}int ReleasePrice::getFrequentRenterPoints(intdaysRented)
{return (daysRented > 1) ? 2: 1;
}//*********************************************影片類*************************************
Movie::Movie(string title, intpriceCode):price(NULL)
{this->title =title;
setPriceCode(priceCode);
}stringMovie::getTitle()
{returntitle;
}void Movie::setTitle(stringvalue)
{
title=value;
}intMovie::getPriceCode()
{return price->getPriceCode();
}void Movie::setPriceCode(intpriceCode)
{if (price !=NULL)deleteprice;switch(priceCode)
{caseREGULAR:
price= newRegularPrice();break;caseCHILDRENS:
price= newChildrensPrice();break;caseNEW_RELEASE:
price= newReleasePrice();break;
}
}double Movie::getCharge(intdaysRented)
{double ret = 0;if (price !=NULL)
ret= price->getCharge(daysRented);returnret;
}int Movie::getFrequentRenterPoints(intdaysRented)
{return price->getFrequentRenterPoints(daysRented);
}
Movie::~Movie()
{deleteprice;
}//********************************租賃類(表示某個顧客租了一部影片)**********************************
Rental::Rental(Movie& movie, intdaysRented):movie(movie)
{this->daysRented =daysRented;
}int Rental::getDaysRented(){returndaysRented;}
Movie&Rental::getMovie()
{returnmovie;
}doubleRental::getCharge()
{returnmovie.getCharge(daysRented);
}//將原Customer類的statement中計算常客積分的代碼移到Rental類
intRental::getFrequentRenterPoints()
{returnmovie.getFrequentRenterPoints(daysRented);
}//*********************************顧客類(用來表示顧客)*************************************//獲得總消費
doubleCustomer::getTotalCharge()
{double result = 0;
vector::iterator iter =rentals.begin();while( iter !=rentals.end())
{
Rental& each = *(*iter);
result+=each.getCharge();++iter;
}returnresult;
}//獲得總積分
intCustomer::getTotalFrequentRenterPointers()
{int result = 0;
vector::iterator iter =rentals.begin();while( iter !=rentals.end())
{
Rental& each = *(*iter);
result+=each.getFrequentRenterPoints();++iter;
}returnresult;
}voidCustomer::cleanUp()
{
vector::iterator iter =rentals.begin();while( iter !=rentals.end())
{delete(*iter);++iter;
}
rentals.clear();
}
Customer::Customer(stringname)
{this->name =name;
}void Customer::addRental(Rental*value)
{
rentals.push_back(value);
}string Customer::getName(){returnname;}//statement(報表),生成租賃的詳單
stringCustomer::statement()
{string ret = "Rental Record for" + name + "\n";
vector::iterator iter =rentals.begin();while( iter !=rentals.end())
{
Rental& each = *(*iter);//顯示每個租賃記錄
ret+= "\t" + each.getMovie().getTitle() + "\t" +numToString(each.getCharge())+ "\n";++iter;
}//增加頁腳注釋
ret += "Amount owed is" + numToString(getTotalCharge()) + "\n";//用getTotalCharge代替totalAmount
ret += "You earned" + numToString(getTotalFrequentRenterPointers()) +"\n";returnret;
}
Customer::~Customer()
{
cleanUp();
}//************************************************初始化數據********************************************
void init(Customer&customer)
{
Movie* mv = new Movie("倚天屠龍記", REGULAR);
mv->setPriceCode(NEW_RELEASE); //重新改變影片的類型為NEW_RELEASE
//這在上個例子是不可能的!
Rental* rt = new Rental(*mv, 2);
customer.addRental(rt);
mv= new Movie("新水滸傳", NEW_RELEASE);
rt= new Rental(*mv, 3);
customer.addRental(rt);
mv= new Movie("喜羊羊與灰太狼", CHILDRENS);
rt= new Rental(*mv, 5);
customer.addRental(rt);
}intmain()
{
Customer customer("SantaClaus");
init(customer);
cout<< customer.statement() <
}/*輸出結果
Rental Record for SantaClaus
倚天屠龍記 6
新水滸傳 9
喜羊羊與灰太狼 4.5
Amount owed is 19.5
You earned 5*/
總結
以上是生活随笔為你收集整理的java多态替换switch_第1章 重构,第一个案例(3):运用多态取代switch的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python测量血压_python距离测
- 下一篇: 生活,真叫人头秃!