設計模式
[TOC]
單例模式
實現(xiàn)1個類只有1個實例化對象 & 提供一個全局訪問點
實現(xiàn)
-
餓漢式
class Singleton { // 1. 加載該類時侵续,單例就會自動被創(chuàng)建 private static Singleton ourInstance = new Singleton(); // 2. 構造函數(shù) 設置為 私有權限 // 原因:禁止他人創(chuàng)建實例 private Singleton() { } // 3. 通過調用靜態(tài)方法獲得創(chuàng)建的單例 public static Singleton newInstance() { return ourInstance; } }
-
枚舉式
public enum Singleton{ //定義1個枚舉的元素,即為單例類的1個實例 INSTANCE; // 隱藏了1個空的爹殊、私有的 構造方法 // private Singleton () {} } // 獲取單例的方式: Singleton singleton = Singleton.INSTANCE;
-
懶漢式
class Singleton { // 1. 類加載時宛徊,先不自動創(chuàng)建單例 // 即,將單例的引用先賦值為 Null private static Singleton ourInstance = null; // 2. 構造函數(shù) 設置為 私有權限 // 原因:禁止他人創(chuàng)建實例 private Singleton() { } // 3. 需要時才手動調用 newInstance() 創(chuàng)建 單例 public static Singleton newInstance() { // 先判斷單例是否為空错英,以避免重復創(chuàng)建 if( ourInstance == null){ ourInstance = new Singleton(); } return ourInstance; } }
-
線程安全:雙重校驗
class Singleton { private static Singleton ourInstance = null菠秒; private Singleton() { } public static Singleton newInstance() { // 加入雙重校驗鎖 // 校驗鎖1:第1個if if( ourInstance == null){ // ① synchronized (Singleton.class){ // ② // 校驗鎖2:第2個 if if( ourInstance == null){ ourInstance = new Singleton(); } } } return ourInstance; } }
-
靜態(tài)內部類
- 在靜態(tài)內部類里創(chuàng)建單例,在裝載該內部類時才會去創(chuàng)建單例
- 線程安全:類是由
JVM
加載戒突,而JVM
只會加載1遍屯碴,保證只有1個單例
class Singleton { // 1. 創(chuàng)建靜態(tài)內部類 private static class Singleton2 { // 在靜態(tài)內部類里創(chuàng)建單例 private static Singleton ourInstance = new Singleton(); } // 私有構造函數(shù) private Singleton() { } // 延遲加載膊存、按需創(chuàng)建 public static Singleton newInstance() { return Singleton2.ourInstance; } }
優(yōu)點
提供了對唯一實例的受控訪問导而;
由于在系統(tǒng)內存中只存在一個對象,因此可以節(jié)約系統(tǒng)資源隔崎,對于一些需要頻繁創(chuàng)建和銷毀的對象單例模式無疑可以提高系統(tǒng)的性能今艺;
可以根據(jù)實際情況需要,在單例模式的基礎上擴展做出雙例模式爵卒,多例模式虚缎;
缺點
- 單例類的職責過重,里面的代碼可能會過于復雜钓株,在一定程度上違背了“單一職責原則”实牡。
- 如果實例化的對象長時間不被利用,會被系統(tǒng)認為是垃圾而被回收轴合,這將導致對象狀態(tài)的丟失创坞。
建造者模式
隱藏創(chuàng)建對象的建造過程 & 細節(jié),使得用戶在不知對象的建造過程 & 細節(jié)的情況下受葛,就可直接創(chuàng)建復雜的對象
- 用戶只需要給出指定復雜對象的類型和內容题涨;
- 建造者模式負責按順序創(chuàng)建復雜對象(把內部的建造過程和細節(jié)隱藏起來)
作用(解決的問題)
降低創(chuàng)建復雜對象的復雜度
隔離了創(chuàng)建對象的構建過程 & 表示
從而:
- 方便用戶創(chuàng)建復雜的對象(不需要知道實現(xiàn)過程)
- 代碼復用性 & 封裝性(將對象構建過程和細節(jié)進行封裝 & 復用)
常用Builder模式:
public class Computer {
private String cpu;
private String mainBoard;
private String HD;
private Computer(String cpu, String mainBoard, String HD) {
this.cpu = cpu;
this.mainBoard = mainBoard;
this.HD = HD;
}
public static class Builder{
private String cpu;
private String mainBoard;
private String HD;
public Builder setCpu(String cpu) {
this.cpu = cpu;
return this;
}
public Builder setMainBoard(String mainBoard) {
this.mainBoard = mainBoard;
return this;
}
public Builder setHD(String HD) {
this.HD = HD;
return this;
}
public Computer build() {
return new Computer(cpu,mainBoard,HD);
}
}
}
public static void main(String[] args) {
Computer computer = new Computer.Builder().setCpu("cpu").setMainBoard("123").setHD("HD").build();
}
實例
- 背景
小成希望去電腦城買一臺組裝的臺式主機 - 過程
- 電腦城老板(Diretor)和小成(Client)進行需求溝通(買來打游戲?學習总滩?看片纲堵?)
- 了解需求后,電腦城老板將小成需要的主機劃分為各個部件(Builder)的建造請求(CPU咳秉、主板blabla)
- 指揮裝機人員(ConcreteBuilder)去構建組件婉支;
- 將組件組裝起來成小成需要的電腦(Product)
使用步驟
步驟1:定義組裝的過程(Builder):組裝電腦的過程
public abstract class Builder {
//第一步:裝CPU
//聲明為抽象方法,具體由子類實現(xiàn)
public abstract void BuildCPU()澜建;
//第二步:裝主板
//聲明為抽象方法向挖,具體由子類實現(xiàn)
public abstract void BuildMainboard()蝌以;
//第三步:裝硬盤
//聲明為抽象方法,具體由子類實現(xiàn)
public abstract void BuildHD()何之;
//返回產(chǎn)品的方法:獲得組裝好的電腦
public abstract Computer GetComputer()跟畅;
}
步驟2: 電腦城老板委派任務給裝機人員(Director)
public class Director{
//指揮裝機人員組裝電腦
public void Construct(Builder builder){
builder. BuildCPU();
builder.BuildMainboard();
builder. BuildHD();
}
}
步驟3: 創(chuàng)建具體的建造者(ConcreteBuilder):裝機人員
//裝機人員1
public class ConcreteBuilder extend Builder{
//創(chuàng)建產(chǎn)品實例
Computer computer = new Computer();
//組裝產(chǎn)品
@Override
public void BuildCPU(){
computer.Add("組裝CPU")
}
@Override
public void BuildMainboard(){
computer.Add("組裝主板")
}
@Override
public void BuildHD(){
computer.Add("組裝主板")
}
//返回組裝成功的電腦
@Override
public Computer GetComputer(){
return computer
}
}
步驟4: 定義具體產(chǎn)品類(Product):電腦
public class Computer{
//電腦組件的集合
private List<String> parts = new ArrayList<String>();
//用于將組件組裝到電腦里
public void Add(String part){
parts.add(part);
}
public void Show(){
for (int i = 0;i<parts.size();i++){
System.out.println(“組件”+parts.get(i)+“裝好了”);
}
System.out.println(“電腦組裝完成溶推,請驗收”);
}
}
步驟5:客戶端調用-小成到電腦城找老板買電腦
public class Builder Pattern{
public static void main(String[] args){
//逛了很久終于發(fā)現(xiàn)一家合適的電腦店
//找到該店的老板和裝機人員
Director director = new Director();
Builder builder = new ConcreteBuilder();
//溝通需求后徊件,老板叫裝機人員去裝電腦
director.Construct(builder);
//裝完后,組裝人員搬來組裝好的電腦
Computer computer = builder.GetComputer();
//組裝人員展示電腦給小成看
computer.Show()蒜危;
}
}
優(yōu)點
- 易于解耦
將產(chǎn)品本身與產(chǎn)品創(chuàng)建過程進行解耦虱痕,可以使用相同的創(chuàng)建過程來得到不同的產(chǎn)品。也就說細節(jié)依賴抽象辐赞。 - 易于精確控制對象的創(chuàng)建
將復雜產(chǎn)品的創(chuàng)建步驟分解在不同的方法中部翘,使得創(chuàng)建過程更加清晰 - 易于拓展
增加新的具體建造者無需修改原有類庫的代碼,易于拓展响委,符合“開閉原則“新思。
每一個具體建造者都相對獨立,而與其他的具體建造者無關赘风,因此可以很方便地替換具體建造者或增加新的具體建造者夹囚,用戶使用不同的具體建造者即可得到不同的產(chǎn)品對象。
缺點
- 建造者模式所創(chuàng)建的產(chǎn)品一般具有較多的共同點邀窃,其組成部分相似荸哟;如果產(chǎn)品之間的差異性很大,則不適合使用建造者模式蛔翅,因此其使用范圍受到一定的限制敲茄。
- 如果產(chǎn)品的內部變化復雜,可能會導致需要定義很多具體建造者類來實現(xiàn)這種變化山析,導致系統(tǒng)變得很龐大。
簡單工廠模式
- 簡單工廠模式又叫靜態(tài)方法模式(因為工廠類定義了一個靜態(tài)方法)
- 現(xiàn)實生活中掏父,工廠是負責生產(chǎn)產(chǎn)品的笋轨;同樣在設計模式中,簡單工廠模式我們可以理解為負責生產(chǎn)對象的一個類赊淑,稱為“工廠類”
實現(xiàn)
步驟1. 創(chuàng)建抽象產(chǎn)品類爵政,定義具體產(chǎn)品的公共接口
abstract class Product{
public abstract void Show();
}
步驟2. 創(chuàng)建具體產(chǎn)品類(繼承抽象產(chǎn)品類),定義生產(chǎn)的具體產(chǎn)品
//具體產(chǎn)品類A
class ProductA extends Product{
@Override
public void Show() {
System.out.println("生產(chǎn)出了產(chǎn)品A");
}
}
//具體產(chǎn)品類B
class ProductB extends Product{
@Override
public void Show() {
System.out.println("生產(chǎn)出了產(chǎn)品C");
}
}
//具體產(chǎn)品類C
class ProductC extends Product{
@Override
public void Show() {
System.out.println("生產(chǎn)出了產(chǎn)品C");
}
}
步驟3. 創(chuàng)建工廠類陶缺,通過創(chuàng)建靜態(tài)方法從而根據(jù)傳入不同參數(shù)創(chuàng)建不同具體產(chǎn)品類的實例
class Factory {
public static Product Manufacture(String ProductName){
//工廠類里用switch語句控制生產(chǎn)哪種商品钾挟;
//使用者只需要調用工廠類的靜態(tài)方法就可以實現(xiàn)產(chǎn)品類的實例化。
switch (ProductName){
case "A":
return new ProductA();
case "B":
return new ProductB();
case "C":
return new ProductC();
default:
return null;
}
}
}
步驟4. 外界通過調用工廠類的靜態(tài)方法饱岸,傳入不同參數(shù)從而創(chuàng)建不同具體產(chǎn)品類的實例
//工廠產(chǎn)品生產(chǎn)流程
public class SimpleFactoryPattern {
public static void main(String[] args){
Factory mFactory = new Factory();
//客戶要產(chǎn)品A
try {
//調用工廠類的靜態(tài)方法 & 傳入不同參數(shù)從而創(chuàng)建產(chǎn)品實例
mFactory.Manufacture("A").Show();
}catch (NullPointerException e){
System.out.println("沒有這一類產(chǎn)品");
}
//客戶要產(chǎn)品B
try {
mFactory.Manufacture("B").Show();
}catch (NullPointerException e){
System.out.println("沒有這一類產(chǎn)品");
}
//客戶要產(chǎn)品C
try {
mFactory.Manufacture("C").Show();
}catch (NullPointerException e){
System.out.println("沒有這一類產(chǎn)品");
}
//客戶要產(chǎn)品D
try {
mFactory.Manufacture("D").Show();
}catch (NullPointerException e){
System.out.println("沒有這一類產(chǎn)品");
}
}
}
優(yōu)點
- 將創(chuàng)建實例的工作與使用實例的工作分開掺出,使用者不必關心類對象如何創(chuàng)建徽千,實現(xiàn)了解耦;
- 把初始化實例時的工作放到工廠里進行汤锨,使代碼更容易維護双抽。 更符合面向對象的原則 & 面向接口編程,而不是面向實現(xiàn)編程闲礼。
缺點
- 工廠類集中了所有實例(產(chǎn)品)的創(chuàng)建邏輯牍汹,一旦這個工廠不能正常工作,整個系統(tǒng)都會受到影響柬泽;
- 違背“開放 - 關閉原則”慎菲,一旦添加新產(chǎn)品就不得不修改工廠類的邏輯,這樣就會造成工廠邏輯過于復雜锨并。
- 簡單工廠模式由于使用了靜態(tài)工廠方法露该,靜態(tài)方法不能被繼承和重寫,會造成工廠角色無法形成基于繼承的等級結構琳疏。
工廠方法模式
解決簡單工廠模式的缺點有决,將類的實例化(具體產(chǎn)品的創(chuàng)建)延遲到工廠類的子類(具體工廠)中完成,即由子類來決定應該實例化(創(chuàng)建)哪一個類空盼。
步驟1: 創(chuàng)建抽象工廠類书幕,定義具體工廠的公共接口
abstract class Factory{
public abstract Product Manufacture();
}
步驟2: 創(chuàng)建抽象產(chǎn)品類 ,定義具體產(chǎn)品的公共接口揽趾;
abstract class Product{
public abstract void Show();
}
步驟3: 創(chuàng)建具體產(chǎn)品類(繼承抽象產(chǎn)品類)台汇, 定義生產(chǎn)的具體產(chǎn)品;
//具體產(chǎn)品A類
class ProductA extends Product{
@Override
public void Show() {
System.out.println("生產(chǎn)出了產(chǎn)品A");
}
}
//具體產(chǎn)品B類
class ProductB extends Product{
@Override
public void Show() {
System.out.println("生產(chǎn)出了產(chǎn)品B");
}
}
步驟4:創(chuàng)建具體工廠類(繼承抽象工廠類)篱瞎,定義創(chuàng)建對應具體產(chǎn)品實例的方法苟呐;
//工廠A類 - 生產(chǎn)A類產(chǎn)品
class FactoryA extends Factory{
@Override
public Product Manufacture() {
return new ProductA();
}
}
//工廠B類 - 生產(chǎn)B類產(chǎn)品
class FactoryB extends Factory{
@Override
public Product Manufacture() {
return new ProductB();
}
}
步驟5:外界通過調用具體工廠類的方法,從而創(chuàng)建不同具體產(chǎn)品類的實例
//生產(chǎn)工作流程
public class FactoryPattern {
public static void main(String[] args){
//客戶要產(chǎn)品A
FactoryA mFactoryA = new FactoryA();
mFactoryA.Manufacture().Show();
//客戶要產(chǎn)品B
FactoryB mFactoryB = new FactoryB();
mFactoryB.Manufacture().Show();
}
}
缺點
- 添加新產(chǎn)品時俐筋,除了增加新產(chǎn)品類外牵素,還要提供與之對應的具體工廠類,系統(tǒng)類的個數(shù)將成對增加澄者,在一定程度上增加了系統(tǒng)的復雜度笆呆;同時,有更多的類需要編譯和運行粱挡,會給系統(tǒng)帶來一些額外的開銷赠幕;
- 由于考慮到系統(tǒng)的可擴展性,需要引入抽象層询筏,在客戶端代碼中均使用抽象層進行定義榕堰,增加了系統(tǒng)的抽象性和理解難度,且在實現(xiàn)時可能需要用到DOM嫌套、反射等技術逆屡,增加了系統(tǒng)的實現(xiàn)難度圾旨。
- 雖然保證了工廠方法內的對修改關閉,但對于使用工廠方法的類康二,如果要更換另外一種產(chǎn)品碳胳,仍然需要修改實例化的具體工廠類;
- 一個具體工廠只能創(chuàng)建一種具體產(chǎn)品
抽象工廠模式
解決工廠方法模式的缺點沫勿,即每個工廠只能創(chuàng)建一類產(chǎn)品挨约。
允許使用抽象的接口來創(chuàng)建一組相關產(chǎn)品,而不需要知道或關心實際生產(chǎn)出的具體產(chǎn)品是什么产雹,這樣就可以從具體產(chǎn)品中被解耦诫惭。
實例
- 背景:小成有兩間塑料加工廠(A廠僅生產(chǎn)容器類產(chǎn)品;B廠僅生產(chǎn)模具類產(chǎn)品)蔓挖;隨著客戶需求的變化夕土,A廠所在地的客戶需要也模具類產(chǎn)品,B廠所在地的客戶也需要容器類產(chǎn)品瘟判;
- 沖突:沒有資源(資金+租位)在當?shù)胤謩e開設多一家注塑分廠
- 解決方案:在原有的兩家塑料廠里增設生產(chǎn)需求的功能怨绣,即A廠能生產(chǎn)容器+模具產(chǎn)品;B廠間能生產(chǎn)模具+容器產(chǎn)品拷获。
步驟1: 創(chuàng)建抽象工廠類篮撑,定義具體工廠的公共接口
abstract class Factory{
public abstract Product ManufactureContainer();
public abstract Product ManufactureMould();
}
步驟2: 創(chuàng)建抽象產(chǎn)品族類 ,定義具體產(chǎn)品的公共接口匆瓜;
abstract class AbstractProduct{
public abstract void Show();
}
步驟3: 創(chuàng)建抽象產(chǎn)品類 赢笨,定義具體產(chǎn)品的公共接口;
//容器產(chǎn)品抽象類
abstract class ContainerProduct extends AbstractProduct{
@Override
public abstract void Show();
}
//模具產(chǎn)品抽象類
abstract class MouldProduct extends AbstractProduct{
@Override
public abstract void Show();
}
步驟4: 創(chuàng)建具體產(chǎn)品類(繼承抽象產(chǎn)品類)驮吱, 定義生產(chǎn)的具體產(chǎn)品茧妒;
//容器產(chǎn)品A類
class ContainerProductA extends ContainerProduct{
@Override
public void Show() {
System.out.println("生產(chǎn)出了容器產(chǎn)品A");
}
}
//容器產(chǎn)品B類
class ContainerProductB extends ContainerProduct{
@Override
public void Show() {
System.out.println("生產(chǎn)出了容器產(chǎn)品B");
}
}
//模具產(chǎn)品A類
class MouldProductA extends MouldProduct{
@Override
public void Show() {
System.out.println("生產(chǎn)出了模具產(chǎn)品A");
}
}
//模具產(chǎn)品B類
class MouldProductB extends MouldProduct{
@Override
public void Show() {
System.out.println("生產(chǎn)出了模具產(chǎn)品B");
}
}
步驟5:創(chuàng)建具體工廠類(繼承抽象工廠類),定義創(chuàng)建對應具體產(chǎn)品實例的方法左冬;
//A廠 - 生產(chǎn)模具+容器產(chǎn)品
class FactoryA extends Factory{
@Override
public Product ManufactureContainer() {
return new ContainerProductA();
}
@Override
public Product ManufactureMould() {
return new MouldProductA();
}
}
//B廠 - 生產(chǎn)模具+容器產(chǎn)品
class FactoryB extends Factory{
@Override
public Product ManufactureContainer() {
return new ContainerProductB();
}
@Override
public Product ManufactureMould() {
return new MouldProductB();
}
}
步驟6:客戶端通過實例化具體的工廠類桐筏,并調用其創(chuàng)建不同目標產(chǎn)品的方法創(chuàng)建不同具體產(chǎn)品類的實例
//生產(chǎn)工作流程
public class AbstractFactoryPattern {
public static void main(String[] args){
FactoryA mFactoryA = new FactoryA();
FactoryB mFactoryB = new FactoryB();
//A廠當?shù)乜蛻粜枰萜鳟a(chǎn)品A
mFactoryA.ManufactureContainer().Show();
//A廠當?shù)乜蛻粜枰>弋a(chǎn)品A
mFactoryA.ManufactureMould().Show();
//B廠當?shù)乜蛻粜枰萜鳟a(chǎn)品B
mFactoryB.ManufactureContainer().Show();
//B廠當?shù)乜蛻粜枰>弋a(chǎn)品B
mFactoryB.ManufactureMould().Show();
}
}
優(yōu)點
降低耦合
抽象工廠模式將具體產(chǎn)品的創(chuàng)建延遲到具體工廠的子類中,這樣將對象的創(chuàng)建封裝起來拇砰,可以減少客戶端與具體產(chǎn)品類之間的依賴九昧,從而使系統(tǒng)耦合度低,這樣更有利于后期的維護和擴展毕匀;-
更符合開-閉原則
新增一種產(chǎn)品類時,只需要增加相應的具體產(chǎn)品類和相應的工廠子類即可簡單工廠模式需要修改工廠類的判斷邏輯
-
符合單一職責原則
每個具體工廠類只負責創(chuàng)建對應的產(chǎn)品簡單工廠中的工廠類存在復雜的switch邏輯判斷
-
不使用靜態(tài)工廠方法癌别,可以形成基于繼承的等級結構皂岔。
簡單工廠模式的工廠類使用靜態(tài)工廠方法
缺點
抽象工廠模式很難支持新種類產(chǎn)品的變化。
這是因為抽象工廠接口中已經(jīng)確定了可以被創(chuàng)建的產(chǎn)品集合展姐,如果需要添加新產(chǎn)品躁垛,此時就必須去修改抽象工廠的接口剖毯,這樣就涉及到抽象工廠類的以及所有子類的改變,這樣也就違背了“開發(fā)——封閉”原則教馆。
對于新的產(chǎn)品族符合開-閉原則逊谋;對于新的產(chǎn)品種類不符合開-閉原則,這一特性稱為開-閉原則的傾斜性土铺。
策略模式
定義一系列算法胶滋,將每個算法封裝到具有公共接口的一系列策略類中,從而使它們可以相互替換 & 讓算法可在不影響客戶端的情況下發(fā)生變化
實例
將算法的責任和本身進行解耦悲敷,使得:
- 算法可獨立于使用外部而變化
- 客戶端方便根據(jù)外部條件選擇不同策略來解決不同問題
策略模式僅僅封裝算法(包括添加 & 刪除)究恤,但策略模式并不決定在何時使用何種算法,算法的選擇由客戶端來決定
實例
- 背景:小成有一家百貨公司后德,最近在定年度的促銷活動
- 沖突:每個節(jié)日用同一個促銷活動太枯燥部宿,沒吸引力
- 解決方案:針對不同節(jié)目使用不同促銷活動進行促銷
步驟1: 定義抽象策略角色(Strategy):百貨公司所有促銷活動的共同接口
public abstract class Strategy {
public abstract void Show();
}
步驟2:定義具體策略角色(Concrete Strategy):每個節(jié)日具體的促銷活動
//為春節(jié)準備的促銷活動A
class StrategyA extends Strategy{
@Override
public void show() {
System.out.println("為春節(jié)準備的促銷活動A");
}
}
//為中秋節(jié)準備的促銷活動B
class StrategyB extends Strategy{
@Override
public void show() {
System.out.println("為中秋節(jié)準備的促銷活動B");
}
}
//為圣誕節(jié)準備的促銷活動C
class StrategyC extends Strategy{
@Override
public void show() {
System.out.println("為圣誕節(jié)準備的促銷活動C");
}
}
步驟3:定義環(huán)境角色(Context):用于連接上下文瓢湃,即把促銷活動推銷給客戶理张,這里可以理解為銷售員
class Context_SalesMan{
//持有抽象策略角色的引用
private Strategy strategy;
//生成銷售員實例時告訴銷售員什么節(jié)日(構造方法)
//使得讓銷售員根據(jù)傳入的參數(shù)(節(jié)日)選擇促銷活動(這里使用一個簡單的工廠模式)
public SalesMan(String festival) {
switch ( festival) {
//春節(jié)就使用春節(jié)促銷活動
case "A":
strategy = new StrategyA();
break;
//中秋節(jié)就使用中秋節(jié)促銷活動
case "B":
strategy = new StrategyB();
break;
//圣誕節(jié)就使用圣誕節(jié)促銷活動
case "C":
strategy = new StrategyC();
break;
}
}
//向客戶展示促銷活動
public void SalesManShow(){
strategy.show();
}
}
步驟4:客戶端調用-讓銷售員進行促銷活動的落地
public class StrategyPattern{
public static void main(String[] args){
Context_SalesMan mSalesMan ;
//春節(jié)來了,使用春節(jié)促銷活動
System.out.println("對于春節(jié):");
mSalesMan = Context_SalesMan SalesMan("A");
mSalesMan.SalesManShow();
//中秋節(jié)來了绵患,使用中秋節(jié)促銷活動
System.out.println("對于中秋節(jié):");
mSalesMan = Context_SalesMan SalesMan("B");
mSalesMan.SalesManShow();
//圣誕節(jié)來了雾叭,使用圣誕節(jié)促銷活動
System.out.println("對于圣誕節(jié):");
mSalesMan = Context_SalesMan SalesMan("C");
mSalesMan.SalesManShow();
}
}
優(yōu)點
- 策略類之間可以自由切換
由于策略類都實現(xiàn)同一個接口,所以使它們之間可以自由切換藏雏。 - 易于擴展
增加一個新的策略只需要添加一個具體的策略類即可拷况,基本不需要改變原有的代碼,符合“開閉原則“ - 避免使用多重條件選擇語句(if else)掘殴,充分體現(xiàn)面向對象設計思想赚瘦。
缺點
- 客戶端必須知道所有的策略類,并自行決定使用哪一個策略類奏寨。
- 策略模式將造成產(chǎn)生很多策略類起意,可以通過使用享元模式在一定程度上減少對象的數(shù)量。
適配器模式
定義一個包裝類病瞳,用于包裝不兼容接口的對象
- 包裝類 = 適配器Adapter揽咕;
- 被包裝對象 = 適配者Adaptee = 被適配的類
作用
把一個類的接口變換成客戶端所期待的另一種接口,從而使原本接口不匹配而無法一起工作的兩個類能夠在一起工作套菜。
適配器模式的形式分為:類的適配器模式 & 對象的適配器模式
解決問題
原本由于接口不兼容而不能一起工作的那些類可以在一起工作
優(yōu)點
- 更好的復用性
系統(tǒng)需要使用現(xiàn)有的類亲善,而此類的接口不符合系統(tǒng)的需要。那么通過適配器模式就可以讓這些功能得到更好的復用逗柴。 - 透明蛹头、簡單
客戶端可以調用同一接口,因而對客戶端來說是透明的。這樣做更簡單 & 更直接 - 更好的擴展性
在實現(xiàn)適配器功能的時候渣蜗,可以調用自己開發(fā)的功能屠尊,從而自然地擴展系統(tǒng)的功能。 - 解耦性
將目標類和適配者類解耦耕拷,通過引入一個適配器類重用現(xiàn)有的適配者類讼昆,而無需修改原有代碼 - 符合開放-關閉原則
同一個適配器可以把適配者類和它的子類都適配到目標接口;可以為不同的目標接口實現(xiàn)不同的適配器骚烧,而不需要修改待適配類
缺點
- 過多的使用適配器浸赫,會讓系統(tǒng)非常零亂,不易整體進行把握
類的適配器模式
使用步驟
步驟1: 創(chuàng)建Target接口止潘;
public interface Target {
//這是源類Adapteee沒有的方法
public void Request();
}
步驟2: 創(chuàng)建源類(Adaptee) 掺炭;
public class Adaptee {
public void SpecificRequest(){
}
}
步驟3: 創(chuàng)建適配器類(Adapter)
//適配器Adapter繼承自Adaptee,同時又實現(xiàn)了目標(Target)接口凭戴。
public class Adapter extends Adaptee implements Target {
//目標接口要求調用Request()這個方法名涧狮,但源類Adaptee沒有方法Request()
//因此適配器補充上這個方法名
//但實際上Request()只是調用源類Adaptee的SpecificRequest()方法的內容
//所以適配器只是將SpecificRequest()方法作了一層封裝,封裝成Target可以調用的Request()而已
@Override
public void Request() {
this.SpecificRequest();
}
}
步驟4:定義具體使用目標類么夫,并通過Adapter類調用所需要的方法從而實現(xiàn)目標
public class AdapterPattern {
public static void main(String[] args){
Target mAdapter = new Adapter()者冤;
mAdapter.Request();
}
}
優(yōu)點
- 使用方便,代碼簡化
僅僅引入一個對象档痪,并不需要額外的字段來引用Adaptee實例
缺點
- 高耦合涉枫,靈活性低
使用對象繼承的方式,是靜態(tài)的定義方式
對象的適配器模式
與類的適配器模式不同的是腐螟,對象的適配器模式不是使用繼承關系連接到Adaptee類愿汰,而是使用委派關系連接到Adaptee類。
步驟1: 創(chuàng)建Target接口乐纸;
public interface Target {
//這是源類Adapteee沒有的方法
public void Request();
}
步驟2: 創(chuàng)建源類(Adaptee) 衬廷;
public class Adaptee {
public void SpecificRequest(){
}
}
步驟3: 創(chuàng)建適配器類(Adapter)(不適用繼承而是委派)
class Adapter implements Target{
// 直接關聯(lián)被適配類
private Adaptee adaptee;
// 可以通過構造函數(shù)傳入具體需要適配的被適配類對象
public Adapter (Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void Request() {
// 這里是使用委托的方式完成特殊功能
this.adaptee.SpecificRequest();
}
}
步驟4:定義具體使用目標類,并通過Adapter類調用所需要的方法從而實現(xiàn)目標
public class AdapterPattern {
public static void main(String[] args){
//需要先創(chuàng)建一個被適配類的對象作為參數(shù)
Target mAdapter = new Adapter(new Adaptee())汽绢;
mAdapter.Request();
}
}
優(yōu)點
- 靈活性高吗跋、低耦合
采用 “對象組合”的方式,是動態(tài)組合方式
缺點
- 使用復雜
需要引入對象實例
特別是需要重新定義Adaptee行為時需要重新定義Adaptee的子類宁昭,并將適配器組合適配
代理模式
給目標對象提供一個代理對象跌宛,并由代理對象控制對目標對象的引用
- 代理對象:起到中介作用,連接客戶端和目標對象
- 例子:電腦桌面的快捷方式积仗。電腦對某個程序提供一個快捷方式(代理對象)疆拘,快捷方式連接客戶端和程序,客戶端通過操作快捷方式就可以操作那個程序
作用
? 防止直接訪問目標對象給系統(tǒng)帶來的不必要復雜性寂曹。
優(yōu)點
- 協(xié)調調用者和被調用者入问,降低了系統(tǒng)的耦合度
- 代理對象作為客戶端和目標對象之間的中介丹锹,起到了保護目標對象的作用
缺點
- 由于在客戶端和真實主題之間增加了代理對象,因此會造成請求的處理速度變慢芬失;
- 實現(xiàn)代理模式需要額外的工作(有些代理模式的實現(xiàn)非常復雜),從而增加了系統(tǒng)實現(xiàn)的復雜度匾灶。
靜態(tài)代理
? 靜態(tài)代理:有一個類文件描述
實例
- 背景:小成希望買一臺最新的頂配Mac電腦
- 沖突:國內還沒上棱烂,只有美國才有
- 解決方案:尋找代購進行購買
步驟1: 創(chuàng)建抽象對象接口(Subject):聲明你(真實對象)需要讓代購(代理對象)幫忙做的事(買Mac)
public interface Subject {
public void buyMac();
}
步驟2: 創(chuàng)建真實對象類(RealSubject),即”我“
public class RealSubject implement Subject{
@Override
public void buyMac() {
System.out.println(”買一臺Mac“);
}
}
步驟3:創(chuàng)建代理對象類(Proxy),即”代購“阶女,并通過代理類創(chuàng)建真實對象實例并訪問其方法
public class Proxy implements Subject{
@Override
public void buyMac{
//引用并創(chuàng)建真實對象實例颊糜,即”我“
RealSubject realSubject = new RealSubject();
//調用真實對象的方法秃踩,進行代理購買Mac
realSubject.buyMac()衬鱼;
//代理對象額外做的操作
this.WrapMac();
}
public void WrapMac(){
System.out.println(”用盒子包裝好Mac“);
}
}
步驟4:客戶端調用
public class ProxyPattern {
public static void main(String[] args){
Subject proxy = new Proxy()憔杨;
proxy.buyMac()鸟赫;
}
}
動態(tài)代理
? 動態(tài)代理:在內存中形成代理類
步驟
- 代理對象和真實對象實現(xiàn)相同接口
- 代理對象 = Proxy.newInstance();
- 使用代理對象執(zhí)行方法
//#ProxyInterface.java
public interface ProxyInterface{
void show();
}
//#RealClass.java
public class RealClass implements ProxyInterface {
@Override
public void show() {
}
}
//#ProxyDemo.java
public class ProxyDemo {
public static void main(String[] args) {
RealClass real = new RealClass();
/***
*Proxy.newProxyInstance 參數(shù)說明
*
* @param loader 類加載器:real.getClass().getClassLoader()
* @param interfaces 實現(xiàn)的接口:real.getClass().getInterfaces()
* @param h :InvocationHandler 代理類執(zhí)行每個方法都會調用此接口中的invoke方法
* @return
*/
ProxyInterface proxy = (ProxyInterface) Proxy.newProxyInstance(real.getClass().getClassLoader(), real.getClass().getInterfaces(), new InvocationHandler() {
/***
*
* @param proxy 當前代理類對象
* @param method 當前代理執(zhí)行的方法
* @param args 當前執(zhí)行方法所傳遞的參數(shù)
* @return 當前方法的返回值
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//針對參數(shù)進行操作
// ...
//針對方法邏輯操作
// ...
//真實對象執(zhí)行方法
Object obj = method.invoke(real, args);
// 針對返回值的操作
// ...
return null;
}
});
}
}
模板方法模式
定義一個模板結構,將具體內容延遲到子類去實現(xiàn)消别。
作用
在不改變模板結構的前提下在子類中重新定義模板中的內容抛蚤。
模板方法模式是基于”繼承“的;
解決的問題
- 提高代碼復用性
將相同部分的代碼放在抽象的父類中寻狂,而將不同的代碼放入不同的子類中 - 實現(xiàn)了反向控制
通過一個父類調用其子類的操作岁经,通過對子類的具體實現(xiàn)擴展不同的行為,實現(xiàn)了反向控制 & 符合“開閉原則”
實例
- 背景:小成希望學炒菜:手撕包菜 & 蒜蓉炒菜心
- 沖突:兩道菜的炒菜步驟有的重復有的卻差異很大蛇券,記不住
- 解決方案:利用代碼記錄下來
使用步驟
步驟1: 創(chuàng)建抽象模板結構(Abstract Class):炒菜的步驟
public abstract class Abstract Class {
//模板方法缀壤,用來控制炒菜的流程 (炒菜的流程是一樣的-復用)
//申明為final,不希望子類覆蓋這個方法纠亚,防止更改流程的執(zhí)行順序
final void cookProcess(){
//第一步:倒油
this.pourOil()塘慕;
//第二步:熱油
this.HeatOil();
//第三步:倒蔬菜
this.pourVegetable();
//第四步:倒調味料
this.pourSauce();
//第五步:翻炒
this.fry();
}
//定義結構里哪些方法是所有過程都是一樣的可復用的菜枷,哪些是需要子類進行實現(xiàn)的
//第一步:倒油是一樣的苍糠,所以直接實現(xiàn)
void pourOil(){
System.out.println("倒油");
}
//第二步:熱油是一樣的,所以直接實現(xiàn)
void HeatOil(){
System.out.println("熱油");
}
//第三步:倒蔬菜是不一樣的(一個下包菜啤誊,一個是下菜心)
//所以聲明為抽象方法岳瞭,具體由子類實現(xiàn)
abstract void pourVegetable();
//第四步:倒調味料是不一樣的(一個下辣椒蚊锹,一個是下蒜蓉)
//所以聲明為抽象方法瞳筏,具體由子類實現(xiàn)
abstract void pourSauce();
//第五步:翻炒是一樣的牡昆,所以直接實現(xiàn)
void fry();{
System.out.println("炒啊炒啊炒到熟啊");
}
}
步驟2: 創(chuàng)建具體模板(Concrete Class),即”手撕包菜“和”蒜蓉炒菜心“的具體步驟
//炒手撕包菜的類
public class ConcreteClass_BaoCai extend Abstract Class{
@Override
public void pourVegetable(){
System.out.println(”下鍋的蔬菜是包菜“);
}
@Override
public void pourSauce(){
System.out.println(”下鍋的醬料是辣椒“);
}
}
//炒蒜蓉菜心的類
public class ConcreteClass_CaiXin extend Abstract Class{
@Override
public void pourVegetable(){
System.out.println(”下鍋的蔬菜是菜心“);
}
@Override
public void pourSauce(){
System.out.println(”下鍋的醬料是蒜蓉“);
}
}
**步驟3: **客戶端調用-炒菜了
public class Template Method{
public static void main(String[] args){
//炒 - 手撕包菜
ConcreteClass_BaoCai BaoCai = new ConcreteClass_BaoCai()姚炕;
BaoCai.cookProcess()摊欠;
//炒 - 蒜蓉菜心
ConcreteClass_ CaiXin = new ConcreteClass_CaiXin();
CaiXin.cookProcess()柱宦;
}
}
優(yōu)點
- 提高代碼復用性
將相同部分的代碼放在抽象的父類中 - 提高了拓展性
將不同的代碼放入不同的子類中些椒,通過對子類的擴展增加新的行為 - 實現(xiàn)了反向控制
通過一個父類調用其子類的操作,通過對子類的擴展增加新的行為掸刊,實現(xiàn)了反向控制 & 符合“開閉原則”
缺點
- 引入了抽象類免糕,每一個不同的實現(xiàn)都需要一個子類來實現(xiàn),導致類的個數(shù)增加忧侧,從而增加了系統(tǒng)實現(xiàn)的復雜度石窑。
外觀模式
定義了一個高層、統(tǒng)一的接口蚓炬,外部與通過這個統(tǒng)一的接口對子系統(tǒng)中的一群接口進行訪問松逊。
通過創(chuàng)建一個統(tǒng)一的外觀類,用來包裝子系統(tǒng)中一個 / 多個復雜的類肯夏,客戶端可通過調用外觀類的方法來調用內部子系統(tǒng)中所有方法
主要作用
- 實現(xiàn)客戶類與子系統(tǒng)類的松耦合
- 降低原有系統(tǒng)的復雜度
- 提高了客戶端使用的便捷性经宏,使得客戶端無須關心子系統(tǒng)的工作細節(jié),通過外觀角色即可調用相關功能熄捍。
- 引入外觀角色之后烛恤,用戶只需要與外觀角色交互;
- 用戶與子系統(tǒng)之間的復雜邏輯關系由外觀角色來實現(xiàn)
解決的問題
- 避免了系統(tǒng)與系統(tǒng)之間的高耦合度
- 使得復雜的子系統(tǒng)用法變得簡單
實例
- 背景:小成的爺爺已經(jīng)80歲了余耽,一個人在家生活:每次都需要打開燈缚柏、打開電視、打開空調碟贾;睡覺時關閉燈币喧、關閉電視、關閉空調袱耽;
- 沖突:行動不方便杀餐,走過去關閉那么多電器很麻煩,代碼如下:
電器類
//燈類
public class SubSystemA_Light {
public void on(){
System.out.println("打開了燈....");
}
public void off(){
System.out.println("關閉了燈....");
}
}
//電視類
public class SubSystemB_Television {
public void on(){
System.out.println("打開了電視....");
}
public void off(){
System.out.println("關閉了電視....");
}
}
//空調類
public class SubSystemC_Aircondition {
public void on(){
System.out.println("打開了電視....");
}
public void off(){
System.out.println("關閉了電視....");
}
}
小成買了一個智能家具控制器(外觀對象/統(tǒng)一接口)給他爺爺朱巨,他爺爺只需要一鍵就能打開/關閉 燈史翘、電視機、空調
- 即用外觀模式來為所有子系統(tǒng)設計一個統(tǒng)一的接口
- 客戶端只需要調用外觀類中的方法就可以了冀续,簡化了客戶端的操作
智能遙控器
public class Facade{
SubSystemA_Light light琼讽;
SubSystemB_Television television ;
SubSystemC_Aircondition aircondition洪唐;
//傳參
public Facade(SubSystemA_Light light,SubSystemB_Television television,SubSystemC_Aircondition aircondition){
this.light = light;
this.television = television ;
this.aircondition =aircondition;
}
//起床后一鍵開電器
public void on(){
System.out.prinln("起床了")钻蹬;
light.on();
television.on();
aircondition.on()凭需;
}
public void off(){
//睡覺時一鍵關電器
System.out.prinln("睡覺了")问欠;
light.off()肝匆;
television.off();
aircondition.off();
}
}
客戶端調用:爺爺使用智能遙控器的時候
public class Facade Pattern{
public static void main(String[] args){
//實例化電器類
SubSystemA_Light light = new SubSystemA_Light();
SubSystemB_Television television = new SubSystemB_Television();
SubSystemC_Aircondition aircondition = new SubSystemC_Aircondition();
//傳參
Facade facade = new Facade(light,television,aircondition);
//客戶端直接與外觀對象進行交互
facade.on();
System.out.prinln("可以看電視了")顺献;
facade.off();
System.out.prinln("可以睡覺了")旗国;
}
}
優(yōu)點
- 降低了客戶類與子系統(tǒng)類的耦合度,實現(xiàn)了子系統(tǒng)與客戶之間的松耦合關系
- 只是提供了一個訪問子系統(tǒng)的統(tǒng)一入口滚澜,并不影響用戶直接使用子系統(tǒng)類
- 減少了與子系統(tǒng)的關聯(lián)對象粗仓,實現(xiàn)了子系統(tǒng)與客戶之間
的松耦合關系,松耦合使得子系統(tǒng)的組件變化不會影響到它的客戶设捐。
- 外觀模式對客戶屏蔽了子系統(tǒng)組件,從而簡化了接口塘淑,減少了客戶處理的對象數(shù)目并使子系統(tǒng)的使用更加簡單萝招。
- 引入外觀角色之后,用戶只需要與外觀角色交互存捺;
- 用戶與子系統(tǒng)之間的復雜邏輯關系由外觀角色來實現(xiàn)
- 降低原有系統(tǒng)的復雜度和系統(tǒng)中的編譯依賴性槐沼,并簡化了系統(tǒng)在不同平臺之間的移植過程
因為編譯一個子系統(tǒng)一般不需要編譯所有其他的子系統(tǒng)。一個子系統(tǒng)的修改對其他子系統(tǒng)沒有任何影響捌治,而且子系統(tǒng)內部變化也不會影響到外觀對象岗钩。
缺點
- 在不引入抽象外觀類的情況下,增加新的子系統(tǒng)可能需要修改外觀類或客戶端的源代碼肖油,違背了“開閉原則”
- 不能很好地限制客戶使用子系統(tǒng)類兼吓,如果對客戶訪問子系統(tǒng)類做太多的限制則減少了可變性和靈活性。