第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 <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的更多相关文章
- ruby代码重构第一课
(文章是从我的个人主页上粘贴过来的, 大家也可以访问我的主页 www.iwangzheng.com) 新手写代码的时候往往会出现很多重复的代码没有提取出来,大师高瞻远瞩总能提点很多有意义的改进,今天重 ...
- 学习ExtjsForVs(第一个案例HelloWord)
第一个案例-Hello Word 1.本次练习以ext-4.0.7为例,首先从网上下载ext包. 2.打开包后将里面的三个文件或文件夹拷贝到项目中. resource文件夹 bootstrap.js ...
- spring boot实战(第一篇)第一个案例
版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[+] spring boot实战(第一篇)第一个案例 前言 写在前面的话 一直想将spring boot相关内容写成一个系列的 ...
- 第4章 TCP/IP通信案例:访问Internet上的Web服务器
第4章 TCP/IP通信案例:访问Internet上的Web服务器 4.2 部署代理服务器 书中为了演示访问Internet上的Web服务器的全过程,使用了squid代理服务器程序模拟了一个代理服务器 ...
- (转)编写Spring的第一个案例并测试Spring的开发环境
http://blog.csdn.net/yerenyuan_pku/article/details/52832145 Spring4.2.5的开发环境搭建好了之后,我们来编写Spring的第一个案例 ...
- javascript进阶教程第三章--匿名和闭包--案例实战
javascript进阶教程第三章--匿名和闭包--案例实战 一.学习任务 通过几个小练习回顾学过的知识点 二.实例 练习1: 实例描述:打开页面后规定时间内弹出一个新窗口,新窗口指定时间后自动关闭. ...
- Java生鲜电商平台-一次代码重构的实战案例
Java生鲜电商平台-一次代码重构的实战案例 说明,Java开源生鲜电商平台-一次代码重构的实战案例,根据实际的例子,分析出重构与抽象,使代码更加的健壮与高效. 1.业务说明 系统原先已有登录功能,我 ...
- Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十五章:第一人称摄像机和动态索引
原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十五章:第一人称摄像机和动态索引 代码工程地址: https://g ...
- 第1章 重构,第一个案例(2):分解并重组statement函数
2. 分解并重组statement (1)提炼switch语句到独立函数(amountFor)和注意事项. ①先找出函数内的局部变量和参数:each和thisAmount,前者在switch语句内未被 ...
随机推荐
- 第一个hadoop 程序
首先检查hadoop是否安装并配置正确然后建立WordCount.java文件里面保存package org.myorg; import java.io.IOException;import java ...
- Scalaz(56)- scalaz-stream: fs2-安全运算,fs2 resource safety
fs2在处理异常及资源使用安全方面也有比较大的改善.fs2 Stream可以有几种方式自行引发异常:直接以函数式方式用fail来引发异常.在纯代码里隐式引发异常或者在运算中引发异常,举例如下: /函数 ...
- PHP中抽象类,接口定义
这里先介绍接口,因为在我最近看的好几本php工具书中都没有提到抽象类. 本人也觉得,在理解了接口后抽象类也非常好理解. 例子代码随便写了一下.例子代码是很ok的,测试过了不会报错,懒得看代码的筒靴们看 ...
- Xdebug文档(五) 代码覆盖分析
代码覆盖分析能在请求时让你知道脚本哪一行(或哪一段)在执行. 相关设置 xdebug.coverage_enable 类型: boolean, 默认值: 1, 始于 Xdebug >= 2.2 ...
- 取消IE默认下载工具为迅雷
需求:企业访问内部WEB系统下载文件时,IE默认下载工具设置为迅雷,则弹出迅雷下载框.现需要直接弹出IE下载保存框. 方案:打开迅雷,进行撤销默认下载工具. 1.迅雷5: 1.1. 打开迅雷下载工具 ...
- 推荐一个内容滚动jquery插件
myslider是一个内容滚动jquery插件,版本0.1.2的每次滚动内容是一行内容,可以是文字,可以是一个链接,还可以是图片. 官方网址:http://keleyi.com/jq/myslider ...
- html5 canvas简易版捕鱼达人游戏源码
插件描述:html5利用canvas写的一个js版本的捕鱼,有积分统计,鱼可以全方位移动,炮会跟着鼠标移动,第一次打开需要鼠标移出背景图,再移入的时候就可以控制炮的转动,因为是用的mouseover触 ...
- UIPickerView的使用(二)
上篇文章 UIPickerView的使用(一)学习了如何创建单列选择器,现在看一下如何创建多列选择器 多列选择器(以二列为例) 1.遵守协议和创建两个数据源 2.创建pickView 3.实现代理 / ...
- SharePoint 2013 自定义扩展菜单(二)
接博文<SharePoint 2013 自定义扩展菜单>,多加了几个例子,方便大家理解. 例七 列表设置菜单扩展(listedit.aspx) 扩展效果 XML描述 <CustomA ...
- 在Autodesk应用程序商店发布基于浏览器的Web应用程序
你一定已经听说过Autodesk应用程序商店了,通过Autodesk应用程序商店,你可以免费下载或购买来自全球的优秀开发者发布的应用程序,来帮助你更快更方便的完成你的工作.而且作为开发者,您也可以在A ...