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 <iostream>
#include <vector>
#include <string>
#include <sstream>
using namespace std; //影片类
class Movie
{
private:
string title; //片名
int priceCode; //价格 public:
enum MovieType{
REGULAR = , //普通片
NEW_RELEASE, //新片
CHILDRENS //儿童片
}; Movie(string title, int priceCode)
{
this->title = title;
this->priceCode = priceCode;
} string getTitle(){return title;}
void setTitle(string value)
{
title = value;
} int getPriceCode(){return priceCode;}
void setPriceCode(int value)
{
this->priceCode = value;
} //将原来Rental类中的getCharge移到该类中,并将租期作为参数传入
//搬到这里来的原因是
//1.switch语句中getPriceCode为本类对象的属性
//2.封装影片类型的变化导致计算方式变化于该类中,从而降低对其他类的影响
double getCharge(int daysRented)
{
double result = ;//相当于statement中的thisamount; switch(getPriceCode())
{
case Movie::REGULAR:
result += ; //普通片基本租金为2元
if(daysRented > ) //超过2天的每天加1.5元
result +=(daysRented - ) * 1.5;
break;
case Movie::NEW_RELEASE:
result += daysRented * ; //新片的租金
break;
case Movie::CHILDRENS:
result += 1.5; //儿童片基本租金为1.5元
if(daysRented > ) //超过3天的每天加1.5元
result +=(daysRented - ) * 1.5;
break;
} return result;
} //将原Rental类中常客积分搬到该类中
//原因是常客积分的计费方式与影片类型有关,也是为了控制当
//影片类型变化时,由于计算方式变化对其他类的影响
int getFrequentRenterPoints(int daysRented)
{
//如果是新片且租期超过1天以上,则额外送1分积分
if ((getPriceCode() == Movie::NEW_RELEASE) &&
daysRented > ) return ;
else return ;
}
}; //租赁类(表示某个顾客租了一部影片)
class Rental
{
private:
Movie& movie; //所租的影片
int daysRented; //租期
public:
Rental(Movie& movie, int daysRented):movie(movie)
{
this->daysRented = daysRented;
} int getDaysRented(){return daysRented;} Movie& getMovie()
{
return movie;
} double getCharge()
{
return movie.getCharge(daysRented);
} //将原Customer类的statement中计算常客积分的代码移到Rental类
int getFrequentRenterPoints()
{
return movie.getFrequentRenterPoints(daysRented);
}
}; //顾客类(用来表示顾客)
class Customer
{
private:
string name; //顾客姓名
vector<Rental*> rentals; //每个租赁记录 //获得总消费
double getTotalCharge()
{
double result = ;
vector<Rental*>::iterator iter = rentals.begin();
while( iter != rentals.end())
{
Rental& each = *(*iter); result += each.getCharge(); ++iter;
}
return result;
} //获得总积分
int getTotalFrequentRenterPointers()
{
int result = ; vector<Rental*>::iterator iter = rentals.begin();
while( iter != rentals.end())
{
Rental& each = *(*iter); result += each.getFrequentRenterPoints(); ++iter;
} return result;
} void cleanUp()
{
vector<Rental*>::iterator iter = rentals.begin();
while( iter != rentals.end())
{
delete(*iter);
++iter;
}
rentals.clear();
} template <typename T>
string numToString(T num)
{
stringstream ss;
ss << num;
return ss.str();
} public:
Customer(string name)
{
this->name = name;
} void addRental(Rental* value)
{
rentals.push_back(value);
} string getName(){return name;} //statement(报表),生成租赁的详单
string statement()
{
string ret = "Rental Record for " + name + "\n"; vector<Rental*>::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"; return ret;
} ~Customer()
{
cleanUp();
}
}; void init(Customer& customer)
{
Movie* mv = new Movie("倚天屠龙记",Movie::REGULAR);
Rental* rt = new Rental(*mv, );
customer.addRental(rt); mv = new Movie("新水浒传",Movie::NEW_RELEASE);
rt = new Rental(*mv, );
customer.addRental(rt); mv = new Movie("喜羊羊与灰太狼",Movie::CHILDRENS);
rt = new Rental(*mv, );
customer.addRental(rt);
} int main()
{
Customer customer("SantaClaus");
init(customer); cout << customer.statement() <<endl; return ;
}
/*输出结果
Rental Record for SantaClaus
倚天屠龙记 2
新水浒传 9
喜羊羊与灰太狼 4.5
Amount owed is 15.5
You earned 4
*/

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 <vector>
#include <string>
#include <sstream>
using namespace std; //影片类
class Movie
{
private:
string title; //片名
//int priceCode; //价格,注意,这里被注释了 public:
enum MovieType{
REGULAR = , //普通片
NEW_RELEASE, //新片
CHILDRENS //儿童片
}; Movie(string title); static Movie* createMovie(string title, int priceCode); string getTitle();
void setTitle(string value); //类型码的“自我封装”(提供取值函数)
virtual int getPriceCode() = ; //将原来Rental类中的getCharge移到该类中,并将租期作为参数传入
//搬到这里来的原因是
//1.switch语句中getPriceCode为本类对象的属性
//2.封装影片类型的变化导致计算方式变化于该类中,从而降低对其他类的影响
virtual double getCharge(int daysRented); int getFrequentRenterPoints(int daysRented);
}; //普通片:用子类取代类型码
class RegularMovie: public Movie
{
public:
RegularMovie(string title);
int getPriceCode();
double getCharge(int daysRented);
}; //儿童片:
class ChildrensMovie: public Movie
{ public:
ChildrensMovie(string title);
int getPriceCode();
double getCharge(int daysRented);
}; //新片
class ReleaseMovie: public Movie
{
public:
ReleaseMovie(string title);
int getPriceCode();
double getCharge(int daysRented);
}; //租赁类(表示某个顾客租了一部影片)
class Rental
{
private:
Movie& movie; //所租的影片
int daysRented; //租期
public:
Rental(Movie& movie, int daysRented); int getDaysRented(); Movie& getMovie(); double getCharge(); //将原Customer类的statement中计算常客积分的代码移到Rental类
int getFrequentRenterPoints();
}; //顾客类(用来表示顾客)
class Customer
{
private:
string name; //顾客姓名
vector<Rental*> rentals; //每个租赁记录 //获得总消费
double getTotalCharge(); //获得总积分
int getTotalFrequentRenterPointers(); void cleanUp(); template <typename T>
string numToString(T num)
{
stringstream ss;
ss << num;
return ss.str();
} public:
Customer(string name); void addRental(Rental* value);
string getName(); //statement(报表),生成租赁的详单
string statement(); ~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 <iostream>
#include "types.h"
using namespace std; //*********************************************影片类*************************************
Movie::Movie(string title)
{
this->title = title;
}
//提供创建子类实例的静态函数(也可以使用工厂方法)
Movie* Movie::createMovie(string title, int priceCode)
{
Movie* ret = NULL;
//利用子类替代switch的分支。我们总是在避免使用switch语句。但这里
//只有一处用到switch,并且只用于决定创建何种对象而没有其他的业务逻辑
//所以这样的switch语句是可以接受的。
switch(priceCode)
{
case Movie::REGULAR:
ret = new RegularMovie(title);
break;
case Movie::CHILDRENS:
ret = new ChildrensMovie(title);
break;
case Movie::NEW_RELEASE:
ret = new ReleaseMovie(title);
break;
} return ret;
} string Movie::getTitle()
{
return title;
} void Movie::setTitle(string value)
{
title = value;
} double Movie::getCharge(int daysRented)
{
return Movie::REGULAR;
} //将原Rental类中常客积分搬到该类中
//原因是常客积分的计费方式与影片类型有关,也是为了控制当
//影片类型变化时,由于计算方式变化对其他类的影响
int Movie::getFrequentRenterPoints(int daysRented)
{
//如果是新片且租期超过1天以上,则额外送1分积分
if ((getPriceCode() == Movie::NEW_RELEASE) &&
daysRented > ) return ;
else return ;
} //普通片:用子类取代类型码
RegularMovie::RegularMovie(string title):Movie(title)
{
} int RegularMovie::getPriceCode()
{
return Movie::REGULAR;
} //使用pushDownMethod(Field)方法将与特定类型码相关的函数推到子类来实现
double RegularMovie::getCharge(int daysRented)
{
double result = ; if(daysRented > ) //超过2天的每天加1.5元
result +=(daysRented - ) * 1.5; return result;
} //儿童片
ChildrensMovie::ChildrensMovie(string title):Movie(title)
{
} int ChildrensMovie::getPriceCode()
{
return Movie::CHILDRENS;
} //使用pushDownMethod(Field)方法将与特定类型码相关的函数推到子类来实现
double ChildrensMovie::getCharge(int daysRented)
{
double result = 1.5;//儿童片基本租金为1.5元 if(daysRented > ) //超过3天的每天加1.5元
result +=(daysRented - ) * 1.5; return result;
} //新片
ReleaseMovie::ReleaseMovie(string title):Movie(title)
{
} int ReleaseMovie::getPriceCode()
{
return Movie::NEW_RELEASE;
}
//使用pushDownMethod(Field)方法将与特定类型码相关的函数推到子类来实现
double ReleaseMovie::getCharge(int daysRented)
{
return daysRented * ; //新片的租金
} //********************************租赁类(表示某个顾客租了一部影片)**********************************
Rental::Rental(Movie& movie, int daysRented):movie(movie)
{
this->daysRented = daysRented;
} int Rental::getDaysRented(){return daysRented;} Movie& Rental::getMovie()
{
return movie;
} double Rental::getCharge()
{
return movie.getCharge(daysRented);
} //将原Customer类的statement中计算常客积分的代码移到Rental类
int Rental::getFrequentRenterPoints()
{
return movie.getFrequentRenterPoints(daysRented);
} //*********************************顾客类(用来表示顾客)*************************************
//获得总消费
double Customer::getTotalCharge()
{
double result = ;
vector<Rental*>::iterator iter = rentals.begin();
while( iter != rentals.end())
{
Rental& each = *(*iter); result += each.getCharge(); ++iter;
}
return result;
} //获得总积分
int Customer::getTotalFrequentRenterPointers()
{
int result = ; vector<Rental*>::iterator iter = rentals.begin();
while( iter != rentals.end())
{
Rental& each = *(*iter); result += each.getFrequentRenterPoints(); ++iter;
} return result;
} void Customer::cleanUp()
{
vector<Rental*>::iterator iter = rentals.begin();
while( iter != rentals.end())
{
delete(*iter);
++iter;
}
rentals.clear();
} Customer::Customer(string name)
{
this->name = name;
} void Customer::addRental(Rental* value)
{
rentals.push_back(value);
} string Customer::getName(){return name;} //statement(报表),生成租赁的详单
string Customer::statement()
{
string ret = "Rental Record for " + name + "\n"; vector<Rental*>::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"; return ret;
} Customer::~Customer()
{
cleanUp();
} //************************************************初始化数据******************************************** void init(Customer& customer)
{
Movie* mv = Movie::createMovie("倚天屠龙记",Movie::REGULAR);
Rental* rt = new Rental(*mv, );
customer.addRental(rt); mv = Movie::createMovie("新水浒传",Movie::NEW_RELEASE);
rt = new Rental(*mv, );
customer.addRental(rt); mv = Movie::createMovie("喜羊羊与灰太狼",Movie::CHILDRENS);
rt = new Rental(*mv, );
customer.addRental(rt);
} int main()
{
Customer customer("SantaClaus");
init(customer); cout << customer.statement() <<endl; return ;
}
/*输出结果
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 <vector>
#include <string>
#include <sstream>
using namespace std; enum MovieType{
REGULAR = , //普通片
NEW_RELEASE, //新片
CHILDRENS //儿童片
}; //price类
class Price
{
public:
virtual int getPriceCode() = ;
virtual double getCharge(int daysRented) = ;
virtual int getFrequentRenterPoints(int daysRented){return ;}
virtual ~Price(){}
}; //普通:
class RegularPrice: public Price
{
public:
int getPriceCode();
double getCharge(int daysRented);
}; //儿童片:
class ChildrensPrice: public Price
{
public:
int getPriceCode();
double getCharge(int daysRented);
}; //新片
class ReleasePrice: public Price
{
public:
int getPriceCode();
double getCharge(int daysRented);
int getFrequentRenterPoints(int daysRented);
}; //影片类
class Movie
{
private:
string title; //片名
Price* price; //价格类 public:
Movie(string title, int priceCode);
~Movie(); string getTitle();
void setTitle(string value); int getPriceCode();
void setPriceCode(int priceCode); double getCharge(int daysRented); int getFrequentRenterPoints(int daysRented);
}; //租赁类(表示某个顾客租了一部影片)
class Rental
{
private:
Movie& movie; //所租的影片
int daysRented; //租期
public:
Rental(Movie& movie, int daysRented); int getDaysRented(); Movie& getMovie(); double getCharge(); //将原Customer类的statement中计算常客积分的代码移到Rental类
int getFrequentRenterPoints();
}; //顾客类(用来表示顾客)
class Customer
{
private:
string name; //顾客姓名
vector<Rental*> rentals; //每个租赁记录 //获得总消费
double getTotalCharge(); //获得总积分
int getTotalFrequentRenterPointers(); void cleanUp(); template <typename T>
string numToString(T num)
{
stringstream ss;
ss << num;
return ss.str();
} public:
Customer(string name); void addRental(Rental* value);
string getName(); //statement(报表),生成租赁的详单
string statement(); ~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 <iostream>
#include "types.h"
using namespace std; //*******************************************价格类**************************************
//普通片:用子类取代类型码
int RegularPrice::getPriceCode()
{
return REGULAR;
} //使用pushDownMethod(Field)方法将与特定类型码相关的函数推到子类来实现
double RegularPrice::getCharge(int daysRented)
{
double result = ; if(daysRented > ) //超过2天的每天加1.5元
result +=(daysRented - ) * 1.5; return result;
} //儿童片
int ChildrensPrice::getPriceCode()
{
return CHILDRENS;
} //使用pushDownMethod(Field)方法将与特定类型码相关的函数推到子类来实现
double ChildrensPrice::getCharge(int daysRented)
{
double result = 1.5;//儿童片基本租金为1.5元 if(daysRented > ) //超过3天的每天加1.5元
result +=(daysRented - ) * 1.5; return result;
} //新片
int ReleasePrice::getPriceCode()
{
return NEW_RELEASE;
}
//使用pushDownMethod(Field)方法将与特定类型码相关的函数推到子类来实现
double ReleasePrice::getCharge(int daysRented)
{
return daysRented * ; //新片的租金
} int ReleasePrice::getFrequentRenterPoints(int daysRented)
{
return (daysRented > ) ? : ;
} //*********************************************影片类*************************************
Movie::Movie(string title, int priceCode):price(NULL)
{
this->title = title;
setPriceCode(priceCode);
} string Movie::getTitle()
{
return title;
} void Movie::setTitle(string value)
{
title = value;
} int Movie::getPriceCode()
{
return price->getPriceCode();
} void Movie::setPriceCode(int priceCode)
{
if (price != NULL)
delete price; switch(priceCode)
{
case REGULAR:
price = new RegularPrice();
break;
case CHILDRENS:
price = new ChildrensPrice();
break;
case NEW_RELEASE:
price = new ReleasePrice();
break;
}
} double Movie::getCharge(int daysRented)
{
double ret = ;
if (price != NULL)
ret = price->getCharge(daysRented);
return ret;
} int Movie::getFrequentRenterPoints(int daysRented)
{
return price->getFrequentRenterPoints(daysRented);
} Movie::~Movie()
{
delete price;
} //********************************租赁类(表示某个顾客租了一部影片)**********************************
Rental::Rental(Movie& movie, int daysRented):movie(movie)
{
this->daysRented = daysRented;
} int Rental::getDaysRented(){return daysRented;} Movie& Rental::getMovie()
{
return movie;
} double Rental::getCharge()
{
return movie.getCharge(daysRented);
} //将原Customer类的statement中计算常客积分的代码移到Rental类
int Rental::getFrequentRenterPoints()
{
return movie.getFrequentRenterPoints(daysRented);
} //*********************************顾客类(用来表示顾客)*************************************
//获得总消费
double Customer::getTotalCharge()
{
double result = ;
vector<Rental*>::iterator iter = rentals.begin();
while( iter != rentals.end())
{
Rental& each = *(*iter); result += each.getCharge(); ++iter;
}
return result;
} //获得总积分
int Customer::getTotalFrequentRenterPointers()
{
int result = ; vector<Rental*>::iterator iter = rentals.begin();
while( iter != rentals.end())
{
Rental& each = *(*iter); result += each.getFrequentRenterPoints(); ++iter;
} return result;
} void Customer::cleanUp()
{
vector<Rental*>::iterator iter = rentals.begin();
while( iter != rentals.end())
{
delete(*iter);
++iter;
}
rentals.clear();
} Customer::Customer(string name)
{
this->name = name;
} void Customer::addRental(Rental* value)
{
rentals.push_back(value);
} string Customer::getName(){return name;} //statement(报表),生成租赁的详单
string Customer::statement()
{
string ret = "Rental Record for " + name + "\n"; vector<Rental*>::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"; return ret;
} Customer::~Customer()
{
cleanUp();
} //************************************************初始化数据******************************************** void init(Customer& customer)
{
Movie* mv = new Movie("倚天屠龙记", REGULAR); mv->setPriceCode(NEW_RELEASE); //重新改变影片的类型为NEW_RELEASE
//这在上个例子是不可能的! Rental* rt = new Rental(*mv, );
customer.addRental(rt); mv = new Movie("新水浒传", NEW_RELEASE);
rt = new Rental(*mv, );
customer.addRental(rt); mv = new Movie("喜羊羊与灰太狼", CHILDRENS);
rt = new Rental(*mv, );
customer.addRental(rt);
} int main()
{
Customer customer("SantaClaus");
init(customer); cout << customer.statement() <<endl; return ;
}
/*输出结果
Rental Record for SantaClaus
倚天屠龙记 6
新水浒传 9
喜羊羊与灰太狼 4.5
Amount owed is 19.5
You earned 5
*/

第1章 重构,第一个案例(3):运用多态取代switch的更多相关文章

  1. ruby代码重构第一课

    (文章是从我的个人主页上粘贴过来的, 大家也可以访问我的主页 www.iwangzheng.com) 新手写代码的时候往往会出现很多重复的代码没有提取出来,大师高瞻远瞩总能提点很多有意义的改进,今天重 ...

  2. 学习ExtjsForVs(第一个案例HelloWord)

    第一个案例-Hello Word 1.本次练习以ext-4.0.7为例,首先从网上下载ext包. 2.打开包后将里面的三个文件或文件夹拷贝到项目中. resource文件夹 bootstrap.js ...

  3. spring boot实战(第一篇)第一个案例

    版权声明:本文为博主原创文章,未经博主允许不得转载.   目录(?)[+]   spring boot实战(第一篇)第一个案例 前言 写在前面的话 一直想将spring boot相关内容写成一个系列的 ...

  4. 第4章 TCP/IP通信案例:访问Internet上的Web服务器

    第4章 TCP/IP通信案例:访问Internet上的Web服务器 4.2 部署代理服务器 书中为了演示访问Internet上的Web服务器的全过程,使用了squid代理服务器程序模拟了一个代理服务器 ...

  5. (转)编写Spring的第一个案例并测试Spring的开发环境

    http://blog.csdn.net/yerenyuan_pku/article/details/52832145 Spring4.2.5的开发环境搭建好了之后,我们来编写Spring的第一个案例 ...

  6. javascript进阶教程第三章--匿名和闭包--案例实战

    javascript进阶教程第三章--匿名和闭包--案例实战 一.学习任务 通过几个小练习回顾学过的知识点 二.实例 练习1: 实例描述:打开页面后规定时间内弹出一个新窗口,新窗口指定时间后自动关闭. ...

  7. Java生鲜电商平台-一次代码重构的实战案例

    Java生鲜电商平台-一次代码重构的实战案例 说明,Java开源生鲜电商平台-一次代码重构的实战案例,根据实际的例子,分析出重构与抽象,使代码更加的健壮与高效. 1.业务说明 系统原先已有登录功能,我 ...

  8. Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十五章:第一人称摄像机和动态索引

    原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十五章:第一人称摄像机和动态索引 代码工程地址: https://g ...

  9. 第1章 重构,第一个案例(2):分解并重组statement函数

    2. 分解并重组statement (1)提炼switch语句到独立函数(amountFor)和注意事项. ①先找出函数内的局部变量和参数:each和thisAmount,前者在switch语句内未被 ...

随机推荐

  1. Zookeeper Api(java)入门与应用(转)

    如何使用 Zookeeper 作为一个分布式的服务框架,主要用来解决分布式集群中应用系统的一致性问题,它能提供基于类似于文件系统的目录节点树方式的数据存储,但是 Zookeeper 并不是用来专门存储 ...

  2. [moka同学笔记]四、Yii2.0课程笔记(魏曦老师教程)[匿名函数的使用操作]

    在评论页面中index.php中 [ 'attribute'=>'status', 'value'=>'status0.name', 'filter'=>Commentstatus: ...

  3. No.023:Merge k Sorted Lists

    问题: Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexit ...

  4. mybatis 中的where标签

    mybatis中的where标签可以去除 开头的 and 或者 or 但是放在后面的不行 失败的: <select id="countNotesByParam" parame ...

  5. Java--Callable与返回值future

    package com; import java.util.concurrent.*; /** * Created by yangyu on 16/11/28. */ /** * Callable a ...

  6. java代码实现栈

    这几天在老家有点事,现在弄完了,继续研究一下数据结构,这次的栈并没有对多线程进行优化,如果有想优化的童鞋可以参考我上一篇文章对队列进行的优化,话不多说,上代码: package com.voole.c ...

  7. Xdebug文档(二)变量显示特性

    Xdebug能替代PHP的var_dump()函数来显示变量值.xdebug的版本包含对不同数据类型作数组元素/对象属性.最大深度和字符串长度以不同颜色标识.这里也有一些功能能很好地处理变量显示. 相 ...

  8. 000.Introduction to ASP.NET Core--【Asp.net core 介绍】

    Introduction to ASP.NET Core Asp.net core 介绍 270 of 282 people found this helpful By Daniel Roth, Ri ...

  9. [翻译]用 Puppet 搭建易管理的服务器基础架构(3)

    我通过伯乐在线翻译了一个Puppet简明教程,一共分为四部分,这是第三部分. 本文由 伯乐在线 - Wing 翻译,黄利民 校稿.未经许可,禁止转载!英文出处:Manuel Kiessling.欢迎加 ...

  10. 1-2 nodejs小节 文件读取

    1.表达式 在命令行输入  node回车后,可以在后边输入相应的表达式,进行运算操作   2.阻塞文件读取 var data=fs.readFileSync('input.txt', 'utf-8') ...