Java設(shè)計(jì)模式之java策略模式詳解
為什么使用策略模式?
實(shí)現(xiàn)某一個(gè)功能有多條途徑,每一條途徑對(duì)應(yīng)一種算法,此時(shí)我們可以使用一種設(shè)計(jì)模式來(lái)實(shí)現(xiàn)靈活地選擇解決途徑,也能夠方便地增加新的解決途徑。
策略模式包含角色
Context(環(huán)境類(lèi)):環(huán)境類(lèi)是使用算法的角色,它在解決某個(gè)問(wèn)題(即實(shí)現(xiàn)某個(gè)方法)時(shí)可以采用多種策略。在環(huán)境類(lèi)中維持一個(gè)對(duì)抽象策略類(lèi)的引用實(shí)例,用于定義所采用的策略。Strategy(抽象策略類(lèi)):它為所支持的算法聲明了抽象方法,是所有策略類(lèi)的父類(lèi),它可以是抽象類(lèi)或具體類(lèi),也可以是接口。環(huán)境類(lèi)通過(guò)抽象策略類(lèi)中聲明的方法在運(yùn)行時(shí)調(diào)用具體策略類(lèi)中實(shí)現(xiàn)的算法。ConcreteStrategy(具體策略類(lèi)):它實(shí)現(xiàn)了在抽象策略類(lèi)中聲明的算法,在運(yùn)行時(shí),具體策略類(lèi)將覆蓋在環(huán)境類(lèi)中定義的抽象策略類(lèi)對(duì)象,使用一種具體的算法實(shí)現(xiàn)某個(gè)業(yè)務(wù)處理。
策略模式的類(lèi)圖

策略模式需要一個(gè)策略接口,不同的策略實(shí)現(xiàn)不同的實(shí)現(xiàn)類(lèi),在具體業(yè)務(wù)環(huán)境中僅持有該策略接口,根據(jù)不同的場(chǎng)景使用不同的實(shí)現(xiàn)類(lèi)即可。
面向接口編程,而不是面向?qū)崿F(xiàn)。
排序案例
對(duì)數(shù)組進(jìn)行排序的算法有很多,但是不同的算法在不同的場(chǎng)景下可以發(fā)揮更大的效率,例如數(shù)據(jù)量很大的時(shí)候,我們可以使用快速排序,數(shù)據(jù)量小的時(shí)候就可以采用插入排序

抽象策略類(lèi)
//抽象策略類(lèi)
public interface Strategy
{
public void sort();
}
具體策略類(lèi)
public class QuickSort implements Strategy
{
@Override
public void sort() {
System.out.println("快速排序");
}
}
public class InsertSort implements Strategy
{
@Override
public void sort() {
System.out.println("插入排序");
}
}
public class BubbleSort implements Strategy
{
@Override
public void sort() {
System.out.println("冒泡排序");
}
}
環(huán)境類(lèi)
public class Context
{
private Strategy strategy;
public void sort(int[] arr,Strategy strategy)
{
this.strategy=strategy;
doSort();
}
private void doSort()
{
strategy.sort();
}
}
測(cè)試類(lèi)
public class Client
{
public static void main(String[] args) {
int[] arr={1,1,1,1,1,1,1,1,1,1};
int[] arr1={1,1,1,1,1,1};
int[] arr2={1,1,1};
Context context=new Context();
context.sort(arr,new QuickSort());
context.sort(arr1,new InsertSort());
context.sort(arr2,new BubbleSort());
}
}

策略模式的優(yōu)點(diǎn)
- 策略模式提供了對(duì) “開(kāi)閉原則” 的完美支持,用戶(hù)可以在不修改原有系統(tǒng)的基礎(chǔ)上選擇算法或行為,也可以靈活地增加新的算法或行為。
- 策略模式提供了管理相關(guān)的算法族的辦法。策略類(lèi)的等級(jí)結(jié)構(gòu)定義了一個(gè)算法或行為族,恰當(dāng)使用繼承可以把公共的代碼移到抽象策略類(lèi)中,從而避免重復(fù)的代碼。
- 策略模式提供了一種可以替換繼承關(guān)系的辦法。如果不使用策略模式而是通過(guò)繼承,這樣算法的使用就 和算法本身混在一起,不符合 “單一職責(zé)原則”,而且使用繼承無(wú)法實(shí)現(xiàn)算法或行為在程序運(yùn)行時(shí)的動(dòng)態(tài)切 換。
- 使用策略模式可以避免多重條件選擇語(yǔ)句。多重條件選擇語(yǔ)句是硬編碼,不易維護(hù)。
- 策略模式提供了一種算法的復(fù)用機(jī)制,由于將算法單獨(dú)提取出來(lái)封裝在策略類(lèi)中,因此不同的環(huán)境類(lèi)可以方便地復(fù)用這些策略類(lèi)。
策略模式的缺點(diǎn)
- 客戶(hù)端必須知道所有的策略類(lèi),并自行決定使用哪一個(gè)策略類(lèi)。這就意味著客戶(hù)端必須理解這些算法的區(qū)別,以便適時(shí)選擇恰當(dāng)?shù)乃惴?。換言之,策略模式只適用于客戶(hù)端知道所有的算法或行為的情況。
- 策略模式將造成系統(tǒng)產(chǎn)生很多具體策略類(lèi),任何細(xì)小的變化都將導(dǎo)致系統(tǒng)要增加一個(gè)新的具體策略類(lèi)。
- 無(wú)法同時(shí)在客戶(hù)端使用多個(gè)策略類(lèi),也就是說(shuō),在使用策略模式時(shí),客戶(hù)端每次只能使用一個(gè)策略類(lèi),不支持使用一個(gè)策略類(lèi)完成部分功能后再使用另一個(gè)策略類(lèi)來(lái)完成剩余功能的情況。
適用場(chǎng)景
- 一個(gè)系統(tǒng)需要?jiǎng)討B(tài)地在幾種算法中選擇一種,那么可以將這些算法封裝到一個(gè)個(gè)的具體算法類(lèi)中,而這些具體算法類(lèi)都是一個(gè)抽象算法類(lèi)的子類(lèi)。換言之,這些具體算法類(lèi)均有統(tǒng)一的接口,根據(jù) “里氏代換原則” 和面向?qū)ο蟮亩鄳B(tài)性,客戶(hù)端可以選擇使用任何一個(gè)具體算法類(lèi),并只需要維持一個(gè)數(shù)據(jù)類(lèi)型是抽象算法類(lèi)的對(duì)象。
- 一個(gè)對(duì)象有很多的行為,如果不用恰當(dāng)?shù)哪J?,這些行為就只好使用多重條件選擇語(yǔ)句來(lái)實(shí)現(xiàn)。此時(shí),使用策略模式,把這些行為轉(zhuǎn)移到相應(yīng)的具體策略類(lèi)里面,就可以避免使用難以維護(hù)的多重條件選擇語(yǔ)句。
- 不希望客戶(hù)端知道復(fù)雜的、與算法相關(guān)的數(shù)據(jù)結(jié)構(gòu),在具體策略類(lèi)中封裝算法與相關(guān)的數(shù)據(jù)結(jié)構(gòu),可以提高算法的保密性與安全性。
源碼分析策略模式的典型應(yīng)用
Java Comparator 中的策略模式
java.util.Comparator 接口是比較器接口,可以通過(guò) Collections.sort(List,Comparator) 和 Arrays.sort(Object[],Comparator) 對(duì)集合和數(shù)據(jù)進(jìn)行排序,下面為示例程序
一個(gè)學(xué)生類(lèi),有兩個(gè)屬性 id 和 name
@Data
@AllArgsConstructor
public class Student {
private Integer id;
private String name;
@Override
public String toString() {
return "{id=" + id + ", name='" + name + "'}";
}
}
實(shí)現(xiàn)兩個(gè)比較器,比較器實(shí)現(xiàn)了 Comparator 接口,一個(gè)升序,一個(gè)降序
// 降序
public class DescSortor implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o2.getId() - o1.getId();
}
}
// 升序
public class AscSortor implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.getId() - o2.getId();
}
}
通過(guò) Arrays.sort() 對(duì)數(shù)組進(jìn)行排序
public class Test1 {
public static void main(String[] args) {
Student[] students = {
new Student(3, "張三"),
new Student(1, "李四"),
new Student(4, "王五"),
new Student(2, "趙六")
};
toString(students, "排序前");
Arrays.sort(students, new AscSortor());
toString(students, "升序后");
Arrays.sort(students, new DescSortor());
toString(students, "降序后");
}
public static void toString(Student[] students, String desc){
for (int i = 0; i < students.length; i++) {
System.out.print(desc + ": " +students[i].toString() + ", ");
}
System.out.println();
}
}

通過(guò) Collections.sort() 對(duì)集合List進(jìn)行排序
public class Client
{
public static void main(String[] args) {
List<Stu> students = Arrays.asList(
new Stu(3, "張三"),
new Stu(1, "李四"),
new Stu(4, "王五"),
new Stu(2, "趙六")
);
toString(students, "排序前");
Collections.sort(students, new AscSortor());
toString(students, "升序后");
Collections.sort(students, new DescSortor());
toString(students, "降序后");
}
public static void toString(List<Stu> students, String desc){
for (Stu student : students) {
System.out.print(desc + ": " + student.toString() + ", ");
}
System.out.println();
}
}

我們向 Collections.sort() 和 Arrays.sort() 分別傳入不同的比較器即可實(shí)現(xiàn)不同的排序效果(升序或降序)
這里 Comparator 接口充當(dāng)了抽象策略角色,兩個(gè)比較器 DescSortor 和 AscSortor 則充當(dāng)了具體策略角色,Collections 和 Arrays 則是環(huán)境角色
參考文章
總結(jié)
本篇文章就到這里了,希望能夠給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
Spring實(shí)現(xiàn)聲明式事務(wù)的方法詳解
這篇文章主要介紹了Spring實(shí)現(xiàn)聲明式事務(wù)的方法詳解,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01
Java中jakarta.validation數(shù)據(jù)校驗(yàn)幾個(gè)主要依賴(lài)包講解
在Java開(kāi)發(fā)中,BeanValidationAPI提供了一套標(biāo)準(zhǔn)的數(shù)據(jù)驗(yàn)證機(jī)制,尤其是通過(guò)JakartaBeanValidation(原HibernateValidator)實(shí)現(xiàn),文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-09-09
Java項(xiàng)目中防止SQL注入的四種方案總結(jié)
SQL注入是一種代碼注入技術(shù),通過(guò)把SQL命令插入到Web表單遞交或輸入域名或頁(yè)面請(qǐng)求的查詢(xún)字符串,最終達(dá)到欺騙服務(wù)器執(zhí)行惡意的SQL命令,下面我們就來(lái)看看如何在項(xiàng)目中防止SQL注入吧2023-10-10
SpringBoot2 整合Nacos組件及環(huán)境搭建和入門(mén)案例解析
這篇文章主要介紹了SpringBoot2 整合Nacos組件,環(huán)境搭建和入門(mén)案例詳解,在整合springboot2時(shí)注意版本 0.2.x.RELEASE 對(duì)應(yīng)的是 Spring Boot 2.x 版本,版本 0.1.x.RELEASE 對(duì)應(yīng)的是 Spring Boot 1.x 版本,具體內(nèi)容詳情跟隨小編一起看看吧2022-03-03
Java中synchronized實(shí)現(xiàn)原理詳解
這篇文章主要介紹了Java中synchronized實(shí)現(xiàn)原理詳解,涉及synchronized實(shí)現(xiàn)同步的基礎(chǔ),Java對(duì)象頭,Monitor,Mark Word,鎖優(yōu)化,自旋鎖等相關(guān)內(nèi)容,具有一定借鑒價(jià)值,需要的朋友可以參考下。2017-11-11
Java經(jīng)典排序算法之冒泡排序代碼實(shí)例
這篇文章主要介紹了Java經(jīng)典排序算法之冒泡排序代碼實(shí)例,相鄰兩元素進(jìn)行比較,如過(guò)左側(cè)元素大于右側(cè)元素,則進(jìn)行交換,每完成一次循環(huán)就將最大元素排在最后,下一次循環(huán)是將其它的數(shù)進(jìn)行類(lèi)似操作,需要的朋友可以參考下2023-11-11
從try-with-resources到ThreadLocal,優(yōu)化你的代碼編寫(xiě)方式
這篇文章主要為大家介紹了從try-with-resources到ThreadLocal,優(yōu)化代碼的編寫(xiě)方式詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04

