七:策略模式(不同等级会员打折算法)
定義:策略模式定義了一系列的算法,并將每一個(gè)算法封裝起來(lái),而且使它們還可以相互替換。策略模式讓算法獨(dú)立于使用它的客戶(hù)而獨(dú)立變化;
下面給出策略模式的類(lèi)圖,引自百度百科。
策略模式在LZ第一次接觸到的時(shí)候,LZ是這么理解的,就是如果我們想往一個(gè)方法當(dāng)中插入隨便一段代碼的話(huà),就是策略模式。即如下形式。
public class MyClass {
public void myMethod(){
System.out.println("方法里的代碼");
//LZ想在這插入一段代碼,而且這個(gè)代碼是可以改變的,想怎么變就怎么變
System.out.println("方法里的代碼");
}
}
在JAVA中,接口可以滿(mǎn)足LZ的這一過(guò)分要求,我們可以設(shè)計(jì)一個(gè)接口,并當(dāng)做參數(shù)傳進(jìn)去,就能達(dá)到這個(gè)效果了。我們來(lái)看,先定義一個(gè)接口。
public interface MyInterface {
//我想插入的代碼
void insertCode();
}
將原來(lái)的類(lèi)改成這樣,傳遞一個(gè)接口進(jìn)去。
public class MyClass {
public void myMethod(MyInterface myInterface){
System.out.println("方法里的代碼");
//你看我是不是插進(jìn)來(lái)一段代碼?而且這段代碼是可以隨便改變的
myInterface.insertCode();
System.out.println("方法里的代碼");
}
}
我們只要實(shí)現(xiàn)了MyInterface這個(gè)接口,在insertCode方法中寫(xiě)入我們想要插進(jìn)去的代碼,再將這個(gè)類(lèi)傳遞給myMethod方法,就可以將我們隨手寫(xiě)的代碼插到這個(gè)方法當(dāng)中。比如這樣。
class InsertCode1 implements MyInterface{
public void insertCode() {
System.out.println("我想插進(jìn)去的代碼,第一種");
}
}
class InsertCode2 implements MyInterface{
public void insertCode() {
System.out.println("我想插進(jìn)去的代碼,第二種");
}
}
這樣我們?cè)谡{(diào)用myMethod方法時(shí)就可以隨意往里面插入代碼了,比如。
//客戶(hù)端調(diào)用
public class Client {
public static void main(String[] args) {
MyClass myClass = new MyClass();
myClass.myMethod(new InsertCode1());
System.out.println("--------------------");
myClass.myMethod(new InsertCode2());
}
}
那么運(yùn)行出來(lái)的結(jié)果就是我們成功的將兩端代碼插入到了myMethod方法中,以上所講的算是JAVA中一種技術(shù)層面的實(shí)現(xiàn),就是傳入一個(gè)接口,封裝代碼。那么既然談到設(shè)計(jì)模式,就要有設(shè)計(jì)模式的應(yīng)用場(chǎng)景,有關(guān)策略模式,所產(chǎn)生的形式就和上述是一模一樣的,只是我們適當(dāng)?shù)慕o予模式的應(yīng)用場(chǎng)景,就會(huì)讓它變的更有價(jià)值。
上面的例子代碼清晰但卻理解起來(lái)很生硬,下面LZ舉一個(gè)具有實(shí)際意義的例子。
就比如我們要做一個(gè)商店的收銀系統(tǒng),這個(gè)商店有普通顧客,會(huì)員,超級(jí)會(huì)員以及金牌會(huì)員的區(qū)別,針對(duì)各個(gè)顧客,有不同的打折方式,并且一個(gè)顧客每在商店消費(fèi)1000就增加一個(gè)級(jí)別,那么我們就可以使用策略模式,因?yàn)椴呗阅J矫枋龅木褪撬惴ǖ牟煌疫@個(gè)算法往往非常繁多,并且可能需要經(jīng)常性的互相替換。
這里我們舉例就采用最簡(jiǎn)單的,以上四種顧客分別采用原價(jià),八折,七折和半價(jià)的收錢(qián)方式。
那么我們首先要有一個(gè)計(jì)算價(jià)格的策略接口,如下。
public interface CalPrice {
//根據(jù)原價(jià)返回一個(gè)最終的價(jià)格
Double calPrice(Double originalPrice);
}
下面我們給出四個(gè)計(jì)算方式。
class Common implements CalPrice{
public Double calPrice(Double originalPrice) {
return originalPrice;
}
}
class Vip implements CalPrice{
public Double calPrice(Double originalPrice) {
return originalPrice * 0.8;
}
}
class SuperVip implements CalPrice{
public Double calPrice(Double originalPrice) {
return originalPrice * 0.7;
}
}
class GoldVip implements CalPrice{
public Double calPrice(Double originalPrice) {
return originalPrice * 0.5;
}
}
以上四種計(jì)算方式非常清晰,分別是原價(jià),八折,七折和半價(jià)。下面我們看客戶(hù)類(lèi),我們需要客戶(hù)類(lèi)幫我們完成客戶(hù)升級(jí)的功能。
//客戶(hù)類(lèi)
public class Customer {
private Double totalAmount = 0D;//客戶(hù)在本商店消費(fèi)的總額
private Double amount = 0D;//客戶(hù)單次消費(fèi)金額
private CalPrice calPrice = new Common();//每個(gè)客戶(hù)都有一個(gè)計(jì)算價(jià)格的策略,初始都是普通計(jì)算,即原價(jià)
//客戶(hù)購(gòu)買(mǎi)商品,就會(huì)增加它的總額
public void buy(Double amount){
this.amount = amount;
totalAmount += amount;
if (totalAmount > 3000) {//3000則改為金牌會(huì)員計(jì)算方式
calPrice = new GoldVip();
}else if (totalAmount > 2000) {//類(lèi)似
calPrice = new SuperVip();
}else if (totalAmount > 1000) {//類(lèi)似
calPrice = new Vip();
}
}
//計(jì)算客戶(hù)最終要付的錢(qián)
public Double calLastAmount(){
return calPrice.calPrice(amount);
}
}
下面我們看客戶(hù)端調(diào)用,系統(tǒng)會(huì)幫我們自動(dòng)調(diào)整收費(fèi)策略。
//客戶(hù)端調(diào)用
public class Client {
public static void main(String[] args) {
Customer customer = new Customer();
customer.buy(500D);
System.out.println("客戶(hù)需要付錢(qián):" + customer.calLastAmount());
customer.buy(1200D);
System.out.println("客戶(hù)需要付錢(qián):" + customer.calLastAmount());
customer.buy(1200D);
System.out.println("客戶(hù)需要付錢(qián):" + customer.calLastAmount());
customer.buy(1200D);
System.out.println("客戶(hù)需要付錢(qián):" + customer.calLastAmount());
}
}
運(yùn)行以后會(huì)發(fā)現(xiàn),第一次是原價(jià),第二次是八折,第三次是七折,最后一次則是半價(jià)。我們這樣設(shè)計(jì)的好處是,客戶(hù)不再依賴(lài)于具體的收費(fèi)策略,依賴(lài)于抽象永遠(yuǎn)是正確的。不過(guò)上述的客戶(hù)類(lèi)實(shí)在有點(diǎn)難看,尤其是buy方法,我們可以使用簡(jiǎn)單工廠來(lái)稍微改進(jìn)一下它。我們建立如下策略工廠。
//我們使用一個(gè)標(biāo)準(zhǔn)的簡(jiǎn)單工廠來(lái)改進(jìn)一下策略模式
public class CalPriceFactory {
private CalPriceFactory(){}
//根據(jù)客戶(hù)的總金額產(chǎn)生相應(yīng)的策略
public static CalPrice createCalPrice(Customer customer){
if (customer.getTotalAmount() > 3000) {//3000則改為金牌會(huì)員計(jì)算方式
return new GoldVip();
}else if (customer.getTotalAmount() > 2000) {//類(lèi)似
return new SuperVip();
}else if (customer.getTotalAmount() > 1000) {//類(lèi)似
return new Vip();
}else {
return new Common();
}
}
}
這樣我們就將制定策略的功能從客戶(hù)類(lèi)分離了出來(lái),我們的客戶(hù)類(lèi)可以變成這樣。
//客戶(hù)類(lèi)
public class Customer {
private Double totalAmount = 0D;//客戶(hù)在本商店消費(fèi)的總額
private Double amount = 0D;//客戶(hù)單次消費(fèi)金額
private CalPrice calPrice = new Common();//每個(gè)客戶(hù)都有一個(gè)計(jì)算價(jià)格的策略,初始都是普通計(jì)算,即原價(jià)
//客戶(hù)購(gòu)買(mǎi)商品,就會(huì)增加它的總額
public void buy(Double amount){
this.amount = amount;
totalAmount += amount;
/* 變化點(diǎn),我們將策略的制定轉(zhuǎn)移給了策略工廠,將這部分責(zé)任分離出去 */
calPrice = CalPriceFactory.createCalPrice(this);
}
//計(jì)算客戶(hù)最終要付的錢(qián)
public Double calLastAmount(){
return calPrice.calPrice(amount);
}
public Double getTotalAmount() {
return totalAmount;
}
public Double getAmount() {
return amount;
}
}
現(xiàn)在比之前來(lái)講,我們的策略模式更加靈活一點(diǎn),但是相信看過(guò)LZ博文的都知道,LZ最不喜歡elseif,所以策略模式也是有缺點(diǎn)的,就是當(dāng)策略改變時(shí),我們需要使用elseif去判斷到底使用哪一個(gè)策略,哪怕使用簡(jiǎn)單工廠,也避免不了這一點(diǎn)。比如我們又添加一類(lèi)會(huì)員,那么你需要去添加elseif。再比如我們的會(huì)員現(xiàn)在打九折了,那么你需要添加一個(gè)九折的策略,這沒(méi)問(wèn)題,我們對(duì)擴(kuò)展開(kāi)放,但是你需要修改elseif的分支,將會(huì)員的策略從八折替換為九折,這是簡(jiǎn)單工廠的詬病,在之前已經(jīng)提到過(guò),對(duì)修改開(kāi)放。
總結(jié)
以上是生活随笔為你收集整理的七:策略模式(不同等级会员打折算法)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 浅谈随机数的生成
- 下一篇: 怎么创建具有真实纹理的CG场景岩石?