目錄
- 設計模式學習(Java實作)
- 1. 什么是設計模式
- 設計模式的意義:
- 使用設計模式的優點:
- 設計模式的基本要素:
- 23種設計模式:
- 2. OOP的七大原則
- 3.單例模式
- A.餓漢式單例模式
- B.懶漢式單例模式
- C.懶漢式單例模式-單鎖
- D.懶漢式單例模式-雙重檢測鎖(DCL)
- E.靜態內部類單例模式
- F.列舉類單例模式
- 4.工廠模式
- 工廠模式的作用:
- 工廠模式的詳細分類:
- 核心本質:
- 三種模式:
- 看代碼其實會清晰很多:
- 簡單工廠模式vs工廠方法模式:
- 5.抽象工廠模式
- 定義:
- 適用場景:
- 代碼演示:
- 抽象工廠模式的優點;
- 抽象工廠模式的缺點:
- 6.建造者模式
- 定義:
- 主要作用:
- 舉個具體例子:
- 角色分析:
- 通過代碼來康康:
- 對建造者模式的一些補充說明:
- 另一種方式:
- 舉個例子:
- 建造者模式的優點
- 建造者模式的缺點
- 建造者模式vs抽象工廠模式
- 7.原型模式
- 先講講深拷貝與淺拷貝的區別:
- 直接上代碼:
- 8.配接器模式
- 配接器模式的作用
- 角色分析
- 代碼演示
- 物件配接器的優點:
- 類配接器的缺點:
- 9.橋接模式
- 定義:
- 多層繼承結構vs橋接模式:
- 上代碼演示一下:
- 橋接模式的好處:
- 橋接模式的劣勢:
- 10.靜態代理模式
- 角色分析:
- 上代碼分析-1:
- 代理模式的好處:
- 代理模式的缺點:
- 上代碼分析-2:
- 11.動態代理模式
- 角色分析
- 動態代理的代理類是動態生成的,不是我們直接寫好的
- 代碼演示
- 動態代理的好處:
- 1. 什么是設計模式
設計模式學習(Java實作)
1. 什么是設計模式
設計模式是對于代碼開發經驗的總結,用于提高代碼的可復用性、可維護性、可讀性、穩健性以及安全性
由GoF(四人幫)合作設計,一共收錄了23種設計模式
設計模式的意義:
設計模式的本質是面向物件設計原則的實際運用,是對類的封裝性、繼承性和多型性以及類的關聯關系和組合關系的充分理解
使用設計模式的優點:
- 可以提高程式員的思維能力、編程能力和設計能力
- 使程式設計更加標準化、代碼編制更加工程化,使軟體開發效率大大提高,從而縮短軟體的開發周期
- 使設計的代碼可重用性高,可讀性強,可靠性高,靈活性好,可維護性強
設計模式的基本要素:
- 模式名稱
- 需要解決的問題
- 解決方案
- 效果
23種設計模式:
- 創建型模式:
(描述如何去創建物件,目的是為了將物件的創建和使用分離)
單例模式、工廠模式、抽象工廠模式、建造者模式、原型模式
- 結構型模式:
(描述如何將類/物件按照某種布局組合成更大的結構)
配接器模式、橋接模式、裝飾模式、組合模式、外觀模式、享元模式、代理模式
- 行為型模式:
(描述類/物件之間的協作,以完成單個類無法完成任務,主要是分配職責)
模板方法模式、命令模式、迭代器模式、觀察者模式、中介者模式、備忘錄模式、解釋器模式、狀態模式、策略模式、職責鏈模式、訪問者模式
2. OOP的七大原則
- 開閉原則:對擴展開放,對修改關閉
(也就是說擴展的代碼盡量不影響原來的代碼,盡量做到獨立)
- 里氏替換原則:繼承必須確保超類所擁有的性質在子類中仍然成立
(也就是說,子類應該是在父類基礎上的拓展,添加新的方法,而盡量不要去修改父類原有的方法等)
- 依賴倒置原則:要面向介面編程,不要面向實作編程
- 單一職責原則:控制類的粒度大小,將物件解耦,提高其內聚性
(也就是說,一個物件不應該承擔很多職責,否則會有很多冗余,粒度太粗)
- 介面隔離原則:要為各個類建立它們需要的專用介面
- 迪米特法則:只和直接相關聯的物件通信,而不跟其他物件通信
- 合成復用原則:盡量先使用組合或者聚合等關聯關系來實作,其次才考慮使用繼承關系來實作
3.單例模式
A.餓漢式單例模式
//餓漢式單例模式
//可能會浪費記憶體
//Hungry.java
public class Hungry{
//先占用空間,不一定使用,可能浪費
private byte[] data1 = new byte[1024 * 1024];
private byte[] data2 = new byte[1024 * 1024];
private byte[] data3 = new byte[1024 * 1024];
private byte[] data4 = new byte[1024 * 1024];
private Hungry(){
}
private final static Hungry HUNGRY = new Hungry();
public static Hungry getInstance(){
return HUNGRY;
}
}
B.懶漢式單例模式
//懶漢式單例模式
//有執行緒安全問題
//LazyMan.java
public class LazyMan{
private LazyMan(){
}
private static LazyMan lazyMan;
public static LazyMan getInstance(){
if(lazyMan == null){
lazyMan = new LazyMan();
}
return lazyMan;
}
}
懶漢式單例模式是執行緒不安全的:
//懶漢式單例模式
//有執行緒安全問題
//LazyMan.java
public class LazyMan{
private LazyMan(){
System.out.println(Thread.currentThread().getName() + "ok");
}
private static LazyMan lazyMan;
public static LazyMan getInstance(){
if(lazyMan == null){
lazyMan = new LazyMan();
}
return lazyMan;
}
//多執行緒并發
public static void main(String[] args){
for(int i = 0;i < 10;i++){
new Thread(() ->{
LazyMan.getInstance();
}).start();
}
}
}
那么,為了解決懶漢式單例模式的執行緒不安全問題,我們就應該給他加鎖,
加鎖也有兩種模式:
C.懶漢式單例模式-單鎖
//LazyMan.java
public class LazyMan{
private LazyMan(){
System.out.println(Thread.currentThread().getName() + "ok");
}
private static LazyMan lazyMan;
public static LazyMan getInstance(){
if(lazyMan == null){
synchronized(LazyMan.class){
lazyMan = new LazyMan();
}
}
return lazyMan;
}
//多執行緒并發
public static void main(String[] args){
for(int i = 0;i < 10;i++){
new Thread(() ->{
LazyMan.getInstance();
}).start();
}
}
}
D.懶漢式單例模式-雙重檢測鎖(DCL)
//雙重檢測鎖模式的懶漢式單例模式(即DCL懶漢式)
//通過加鎖來解決懶漢式單例模式的執行緒不安全問題
//LazyMan.java
public class LazyMan{
private LazyMan(){
System.out.println(Thread.currentThread().getName() + "ok");
}
private volatile static LazyMan lazyMan;
public static LazyMan getInstance(){
if(lazyMan == null){
synchronized(LazyMan.class){
if(lazyMan == null){
lazyMan = new LazyMan();
}
}
}
return lazyMan;
}
//多執行緒并發
public static void main(String[] args){
for(int i = 0;i < 10;i++){
new Thread(() ->{
LazyMan.getInstance();
}).start();
}
}
}
關于volatile:
其實,嚴格來說,就算是DCL懶漢式單例模式也是執行緒不安全的,為什么呢?
因為lazyMan = new LazyMan()這個操作并不是原子性操作,它需要走三步:
- 分配記憶體空間
- 執行構造方法,初始化物件
- 把這個物件指向所分配的空間
所以有可能發生指令重排!
可能執行順序是:123
也可能是:312
所以需要volatile~
E.靜態內部類單例模式
public class Holder{
private Holder(){
}
public static Holder getInstance(){
return InnnerClass.HOLDER;
}
public static class InnnerClass{
private static final Holder HOLDER = new Holder();
}
}
以上單例模式都不安全,都能被反射破壞,所以要看看列舉類單例模式!它不會被反射破壞!
F.列舉類單例模式
//EnumSingle.java
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public enum EnumSingle{
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}
class Test{
public static void main(String[] args) throws NoSuchMethodException,IllegalAccessException,InvocationTargetException,InstantiationException{
EnumSingle instance1 = EnumSingle.INSTANCE;
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
declaredConstructor.setAccessible(true);
EnumSingle instance2 = declaredConstructor.newInstance();
System.out.println(instance1);
System.out.println(instance2);
}
}
4.工廠模式
工廠模式的作用:
實作了創建者和呼叫者的分離
工廠模式的詳細分類:
- 簡單工廠模式
- 工廠方法模式
- 抽象工廠模式
核心本質:
- 實體化物件不使用new,用工廠方法代替
- 將選擇實作類、創建物件統一管理和控制,從而將呼叫者跟我們的實作類解耦
三種模式:
- 簡單工廠模式:(靜態工廠模式)
用來生產同一等級結構中的任意產品
- 工廠方法模式:
用來生產同一等級結構中的固定產品
- 抽象工廠模式:
圍繞一個超級工廠創建其他工廠,該超級工廠又稱為其他工廠的工廠
(下一節拓展開來講)
看代碼其實會清晰很多:
//簡單工廠模式 == 靜態工廠模式
//若果需要增加一個新的產品,在不修改代碼的情況下是做不到的
//不滿足開閉原則
public class CarFactory{
public static Car getCar(String car){
if(car.equals("五菱")){
return new WuLing();
}
else if(car.equals("特斯拉")){
return new Tesla();
}
else {
return null;
}
}
}
public Customer{
public static void main(String[] args){
//使用new創建
//Car car = new WuLing();
//Car car2 = new Tesla();
//使用工廠創建
Car car = CarFactory.getCar("五菱");
Car car = CarFactory.getCar("特斯拉");
car.name();
car2.name();
}
}
//工廠方法模式
//滿足了開閉原則,但是增加了代碼量
public class Consumer{
public static void main(String[] args){
Car car = new TeslaFactory().getCar();
Car car2 = new WulingFactory().getCar();
Car car3 = new MobaiFactory().getCar();
}
}
public interface CarFactory{
Car getCar();
}
public class TeslaFactory implements CarFactory{
@Override
public Car getCar(){
return new Tesla();
}
}
public class WulingFactory implements CarFactory{
@Override
public Car getCar(){
return new WuLing();
}
}
public class MobaiFactory implements CarFactory{
@Override
public Car getCar(){
return new Mobai();
}
}
簡單工廠模式vs工廠方法模式:
結構復雜度:簡單工廠模式較好
代碼復雜度:簡單工廠模式較好
編程復雜度:簡單工廠模式較好
管理復雜度:簡單工廠模式較好
根據設計原則來看:工廠方法模式更好
根據實際業務來看:簡單工廠模式更好
5.抽象工廠模式
定義:
抽象工廠模式提供了一個創建一系列相關或者相互依賴物件的介面,無需指定它們具體的類
(也就是把工廠再進行抽象,建立工廠的工廠)
適用場景:
- 客戶端(應用層)不依賴于產品類實體如何被創建、實作等細節
- 強調一系列相關的產品物件(屬于同一產品族)一起使用創建物件需要大量的重復代碼
- 提供一個產品類的庫,所有的產品以同樣的介面出現,從而使得客戶端不依賴于具體的實作
什么是產品族&產品等級結構呢?
產品族:是指同一個工廠生產的,位于不同產品等級結構中的一組產品(比如iphone、ipad、macbook是一個產品族)
產品等級結構:即產品的繼承結構
圖示:

代碼演示:
//抽象工廠模式
//手機產品介面
//IphoneProduct.java
public interface IphoneProduct{
void start();
void shutdown();
void call();
void sendMessage();
}
//路由器產品介面
//IrouterProduct.java
public interface IrouterProduct{
void start();
void shutdown();
void openWiFi();
void setting();
}
//小米手機
//XiaomiPhone.java
public class XiaomiPhone implements IphoneProduct{
@Override
public void start(){
System.out.println("開啟小米手機");
}
@Override
public void shutdown(){
System.out.println("關閉小米手機");
}
@Override
public void call(){
System.out.println("小米打電話");
}
@Override
public void sendMessage(){
System.out.println("小米發資訊");
}
}
//華為手機
//HuaweiPhone.java
public class HuaweiPhone implements IphoneProduct{
@Override
public void start(){
System.out.println("開啟華為手機");
}
@Override
public void shutdown(){
System.out.println("關倍訓為手機");
}
@Override
public void call(){
System.out.println("華為打電話");
}
@Override
public void sendMessage(){
System.out.println("華為發資訊");
}
}
//小米路由器
//XiaomiRouter.java
public class XiaomiRouter implements IrouterProduct{
@Override
public void start(){
System.out.println("啟動小米路由器");
}
@Override
public void shutdown(){
System.out.println("關閉小米路由器");
}
@Override
public void openWiFi(){
System.out.println("打開小米Wifi");
}
@Override
public void setting(){
System.out.println("設定小米路由器");
}
}
//華為路由器
//HuaweiRouter.java
public class HuaweiRouter implements IrouterProduct{
@Override
public void start(){
System.out.println("啟動華為路由器");
}
@Override
public void shutdown(){
System.out.println("關倍訓為路由器");
}
@Override
public void openWiFi(){
System.out.println("打開華為Wifi");
}
@Override
public void setting(){
System.out.println("設定華為路由器");
}
}
//生產抽象產品工廠
//IProductFactory.java
public interface IProductFactory{
//生產手機
IphoneProduct iphoneProduct();
//生產路由器
IrouterProduct irouterProduct();
}
//XiaomiFactory.java
public class XiaomiFactory implements IProductFactory{
@Override
public IphoneProduct iphoneProduct(){
return new XiaomiPhone();
}
@Override
public IrouterProduct irouterProduct(){
return new XiaomiRouter();
}
}
//HuaweiFactory.java
public class HuaweiFactory implements IProductFactory{
@Override
public IphoneProduct iphoneProduct(){
return new HuaweiPhone();
}
@Override
public IrouterProduct irouterProduct(){
return new HuaweiRouter();
}
}
//Client.java
public class Client{
public static void main(String[] args)
System.out.println("=========小米系列產品=========");
//小米工廠
XiaomiFactory xiaomiFactory = new XiaomiFactory();
IphoneProduct iphoneProduct = xiaomiFactory.iphoneProduct();
iphoneProduct.call();
iphoneProduct.sendMessage();
IrouterProduct irouterProduct = xiaomiFactory.irouterProduct();
irouterProduct.openWiFi();
irouterProduct.setting();
System.out.println("=========華為系列產品=========");
//小米工廠
HuaweiFactory huaweiFactory = new huaweiFactory();
IphoneProduct iphoneProduct = huaweiFactory.iphoneProduct();
iphoneProduct.call();
iphoneProduct.sendMessage();
IrouterProduct irouterProduct = huaweiFactory.irouterProduct();
irouterProduct.openWiFi();
irouterProduct.setting();
}
抽象工廠模式的優點;
- 具體產品在應用層的代碼隔離,無需關心創建的細節
- 將一個系列的產品統一到一起創建
抽象工廠模式的缺點:
- 規定了所有可能被創建的產品集合,產品族中想要拓展新的產品比較困難(所以會比較適合比較穩定的系統)
- 增加了系統的抽象性和理解難度
6.建造者模式
建造者模式提供了一種創建物件的最佳方式
用戶只需要給出指定復雜物件的型別和內容,建造者模式復雜按順序創建復雜物件(把內部的建造程序和細節隱藏起來)
定義:
將一個復雜物件的構建和它的表示分離,使得同樣的構建程序可以創建不同的表示
主要作用:
在用戶不知道物件的建造程序和細節的情況下就可能直接創建復雜的物件
舉個具體例子:
想要造一棟房子,就需要建筑公司(指揮者)指揮工人(具體建造者)去造房子(產品)
角色分析:

通過代碼來康康:
//抽象的建造者
//Builder.java
public abstract class Builder{
abstract void buildA(); //打地基
abstract void buildB(); //鋼筋
abstract void buildC(); //鋪電線
abstract void buildD(); //粉刷
abstract Product getProduct();
}
//產品:房子
public class Product{
private String buildA;
private String buildB;
private String buildC;
private String buildD;
public String getBuildA(){
return buildA;
}
public void setBuildA(String buildA){
this.buildA = buildA;
}
public String getBuildB(){
return buildB;
}
public void setBuildB(String buildB){
this.buildB = buildB;
}
public String getBuildC(){
return buildC;
}
public void setBuildC(String buildC){
this.buildC = buildC;
}
public String getBuildD(){
return buildD;
}
public void setBuildD(String buildD){
this.buildD = buildD;
}
@Override
public String toString(){
return "Product{" +
"buildA = '" + buildA + '\'' +
",buildB = '" + buildB + '\'' +
",buildC = '" + buildC + '\'' +
",buildD = '" + buildD + '\'' +
'}';
}
}
//Worker.java
public class Worker extends Builder{
private Product product;
public Worker(){
product = new Product();
}
@Override
void buildA(){
product.setBuildA("打地基");
System.out.println("打地基");
}
@Override
void buildB(){
product.setBuildB("鋼筋");
System.out.println("鋼筋");
}
@Override
void buildC(){
product.setBuildC("鋪電線");
System.out.println("鋪電線");
}
@Override
void buildD(){
product.setBuildD("粉刷");
System.out.println("粉刷");
}
@Override
Product getProduct(){
return product;
}
}
//指揮:負責指揮構建一個工程,描述和決定工程如何構建
//Director.java
public class Director{
//指揮工人按照順序建房子
public Product build(Builer builder){
builder.buildA();
builder.buildB();
builder.buildC();
builder.buildD();
return builder.getProduct();
}
}
//Test.java
public class Test{
public static void main(String[] args){
Director director = new Director();
Product build = director.build(new Worker());
System.out.println(build.toString());
}
}
對建造者模式的一些補充說明:
- Director類在Builder模式中具有很重要的作用,用于指導具體建造者如何建造產品,控制呼叫先后順序,并向呼叫者回傳完整的產品類,
有些情況下需要簡化系統結構,可以把Director和抽象建造者結合- 通過靜態內部類方式實作零件無序裝配構造,這種方式使用起來更加靈活,更符合定義,
內部有復雜物件的默認實作,使用時可以根據用戶需求自由定義更改內容,且無需改變具體的構造方式,就能生產出不同的復雜產品
另一種方式:
舉個例子:
//Builder.java
public abstract class Builder{
abstract Builder buildA(String msg); //漢堡
abstract Builder buildB(String msg); //可樂
abstract Builder buildC(String msg); //薯條
abstract Builder buildD(String msg); //甜點
abstract Product getProduct();
}
//Worker.java
public class Worker extends Builder{
private Product product;
public Worker(){
product = new Product();
}
@Override
Builder buildA(String msg){
product.setBuildA(msg);
return this;
}
@Override
Builder buildB(String msg){
product.setBuildB(msg);
return this;
}
@Override
Builder buildC(String msg){
product.setBuildC(msg);
return this;
}
@Override
Builder buildD(String msg){
product.setBuildD(msg);
return this;
}
@Override
Product getProduct(){
return product;
}
}
//產品:套餐
public class Product{
private String BuildA = "漢堡";
private String BuildB = "可樂";
private String BuildC = "薯條";
private String BuildD = "甜點";
public String getBuildA(){
return BuildA;
}
public void setBuildA(String buildA){
BuildA = buildA;
}
public String getBuildB(){
return BuildB;
}
public void setBuildB(String buildB){
BuildB = buildB;
}
public String getBuildC(){
return BuildC;
}
public void setBuildC(String buildC){
BuildC = buildC;
}
public String getBuildD(){
return BuildD;
}
public void setBuildD(String buildD){
BuildD = buildD;
}
@Override
public String toString(){
return "Product{" +
"BuildA = '" + BuildA + '\'' +
",BuildB = '" + BuildB + '\'' +
",BuildC = '" + BuildC + '\'' +
",BuildD = '" + BuildD + '\'' +
'}';
}
}
//Test.java
public class Test{
public static void main(String[] args){
//服務員
Worker worker = new Worker();
//默認搭配套餐
Product product1 = worker.getProduct();
//自由搭配套餐:鏈式編程
Product product2 = worker.buildA(msg:"全家桶").buildB("雪碧").getProduct();
System.out.println(product1.toString());
System.out.println(product2.toString());
}
}
建造者模式的優點
- 產品的建造和表示分離,實作了解耦,使用建造者模式可以使客戶端不必知道產品內部組成的細節
- 將復雜的創建步驟分解在不同的方法中,使得創建程序更加清晰
- 具體的建造者類之間是相互獨立的,這有利于紫銅的擴展,增加新的具體建造者無需修改原有類別庫的代碼,符合“開閉原則”
建造者模式的缺點
- 建造者模式所創建的產品一般具有較多的共同點,其組成部分相似;如果產品之間的差異性很大,則不適合使用建造者模式,因此其使用范圍收到一定的限制
- 如果產品的內部變化復雜,可能會導致需要定義很多具體建造者來實作這種變化,導致系統變得很龐大
建造者模式vs抽象工廠模式
- 與抽象工廠模式相比,建造者模式回傳一個組裝好的完整產品,而抽象工廠模式回傳一系列相關的產品,這些產品位于不同的產品等級結構,構成了一個產品族
- 在抽象工廠模式中,客戶端實體化工廠類,然后呼叫工廠方法獲取所需產品物件,而在建造者模式中,客戶端可以不呼叫建造者的相關方法,而是通過指揮者類來指導如何生成物件,包括物件的組裝程序和建造步驟,它側重于一步步構造一個復雜物件,回傳一個完整的物件
- 如果將抽象工廠模式看成汽車配件生產工廠,生產一個產品族的產品,那么建造者模式就是一個汽車組裝工廠,通過對部件的組裝可以回傳一輛完整的汽車
7.原型模式
先講講深拷貝與淺拷貝的區別:
圖示
- 淺拷貝:

- 深拷貝:

直接上代碼:
- 淺拷貝:
//原型模式
/*
1. 實作一個介面:cloneable
2. 重寫一個方法:clone()
*/
import java.util.Date;
public class Video implements Cloneable{
private String name;
private Date createTime;
@Override
protected Object clone() throws ClassNotFoundException{
return super.clone();
}
Video(){
}
public Video(String name,Date createTime){
this.name = name;
this.createTime = createTime;
}
public String getName(){
return name;
}
public Date getCreateTime(){
return createTime;
}
public void setName(String name){
this.name = name;
}
public void setCreateTime(Date createTime){
this.createTime = createTime;
}
public String toString(){
return "Video{" + name + '\'' + ", createTime = " + createTime + '}';
}
}
//淺拷貝
//客戶端:克隆
public class Bilibili{
public static void main(String[] args){
//原型物件
Date date = new Date();
Video v1 = new Video(name:"濤濤說Java",date);
Video v2 = (Video) v1.clone();
System.out.println("v1 => " + v1);
System.out.println("v1 => hash: " + v1.hashCode());
System.out.println("v2 => " + v2);
System.out.println("v2 => hash:" + v2.hashCode());
System.out.println("======================");
date.setCreateTime(123456);
System.out.println("v1 => " + v1);
System.out.println("v1 => hash: " + v1.hashCode());
System.out.println("v2 => " + v2);
System.out.println("v2 => hash:" + v2.hashCode());
}
}
- 深拷貝:
import java.util.Date;
public class Video implements Cloneable{
private String name;
private Date createTime;
//對clone()方法的重寫 實作深克隆
@Override
protected Object clone() throws ClassNotFoundException{
Object obj = super.clone();
Video v = (Video) obj;
v.createTime = (Date) this.createTime.clone();
}
Video(){
}
public Video(String name,Date createTime){
this.name = name;
this.createTime = createTime;
}
public String getName(){
return name;
}
public Date getCreateTime(){
return createTime;
}
public void setName(String name){
this.name = name;
}
public void setCreateTime(Date createTime){
this.createTime = createTime;
}
public String toString(){
return "Video{" + name + '\'' + ", createTime = " + createTime + '}';
}
}
//深拷貝(改造clone()方法,也就是將這個物件的屬性也一起克隆)
public class Bilibili{
public static void main(String[] args){
//原型物件
Date date = new Date();
Video v1 = new Video(name:"濤濤說Java",date);
Video v2 = (Video) v1.clone();
System.out.println("v1 => " + v1);
System.out.println("v1 => hash: " + v1.hashCode());
System.out.println("v2 => " + v2);
System.out.println("v2 => hash:" + v2.hashCode());
System.out.println("======================");
date.setCreateTime(123456);
System.out.println("v1 => " + v1);
System.out.println("v1 => hash: " + v1.hashCode());
System.out.println("v2 => " + v2);
System.out.println("v2 => hash:" + v2.hashCode());
}
}
8.配接器模式
配接器模式的作用
將一個類的介面轉換成客戶希望的另一個介面,
Adapter模式使得原本由于介面不兼容而不能一起作業的那些類可以在一起作業
角色分析
- 目標介面:客戶所期待的介面,目標可以是具體的或抽象的類,也可以是介面
- 需要適配的類:需要適配的類或配接器類
- 配接器:通過包裝一個需要適配的物件,把原介面轉換成目標物件
圖示:

代碼演示
//要被適配的類:網線
//Adaptee.java
public class Adaptee{
public void request(){
System.out.println("連接網線上網");
}
}
//Computer.java
public class Computer{
//電腦需要連接上轉接器才能上網
public void net(NetToUsb adapter){
//上網的具體實作,但是得先找一個轉接頭
adapter.handleRequest();
}
public static void main(String[] args){
Computer computer = new Computer(); //電腦
Adaptee adaptee = new Adaptee(); //網線
Adapter adapter = new Adapter(); //轉接器
computer.net(adapter);
}
}
//介面轉換器的抽象實作
//NetToUsb.java
public interface NetToUsb{
//處理請求,網線插到Usb上
public void handleRequest();
}
//真正的配接器,需要連接USB還有網線
//Adapter.java
//1.繼承(類配接器,單繼承)
public class Adapter extends Adaptee implements NetToUsb{
@Override
public void handleRequest(){
super.request();
//可以上網了
}
}
另一種實作方式(better)
//Adapter2.java
//2.組合(物件配接器,常用)
public class Adapter2 implements NetToUsb{
private Adaptee adaptee;
public Adapter2(Adaptee adaptee){
this.adaptee = adaptee;
}
@Override
public void handleRequest(){
adaptee.request();
//可以上網了
}
}
//客戶端類:想上網,但是插不上網線(所以才需要一個配接器,也就是轉接頭)
//Computer.java
public class Computer{
//電腦需要連接上轉接器才能上網
public void net(NetToUsb adapter){
//上網的具體實作,但是得先找一個轉接頭
adapter.handleRequest();
}
public static void main(String[] args){
Computer computer = new Computer(); //電腦
Adaptee adaptee = new Adaptee(); //網線
Adapter2 adapter = new Adapter2(adaptee); //轉接器
computer.net(adapter);
}
}
物件配接器的優點:
- 一個物件配接器可以把多個不同的配接器適配到同一個目標
- 可以適配一個適配者的子類,由于配接器和適配者之間是關聯關系,根據“里氏代換原則”,適配者的子類也可以通過該配接器進行適配
類配接器的缺點:
- 對于Java,C#等不支持多重繼承的語言,一次最多只能適配yige配接器類,不能同時適配多個適配者
- 在Java,C#等語言中,類配接器模式中的目標抽象類只能為介面,不能為類,其使用具有一定的局限性
9.橋接模式
定義:
是將抽象部分與它的實作部分分離,使它們都可以獨立地變化
又稱為柄體(Handle and Body)模式或介面(interface)模式
多層繼承結構vs橋接模式:
圖示:
- 多層繼承結構:

- 橋接模式:

上代碼演示一下:
//橋接模式
public interface Brand{
void info();
}
//聯想品牌
public class Lenovo implements Brand{
@Override
public void info(){
System.out.print("聯想");
}
}
//蘋果品牌
public class Apple implements Brand{
@Override
public void info(){
System.out.print("蘋果");
}
}
//抽象的電腦型別
public abstract Computer{
//組合
protected Brand brand;
public Computer (Brand brand){
this.brand = brand;
}
public void info(){
//自帶品牌
brand.info();
}
}
class Desktop extends Computer{
public Desktop(Brand brand){
super(brand);
}
@Override
public void info(){
super.info();
System.out.println("臺式機");
}
}
class Laptop extends Computer{
public Laptop(Brand brand){
super(brand);
}
@Override
public void info(){
super.info();
System.out.println("筆記本");
}
}
public class Test{
public static void main(String[] args){
//蘋果筆記本
Computer computer = new Laptop(new Apple());
computer.info();
//聯想臺式機
Computer computer2 = new Desktop(new Lenovo());
computer2.info();
}
}
橋接模式的好處:
- 橋接模式是比多繼承方案更好的解決方法,極大地減少了子類的個數,從而降低管理和維護的成本,不像多繼承方案,違背了類的單一職責原則,復用性較差,類的個數也很多;
- 橋接模式提高了系統的擴充性,在兩個變化維度中任意擴展一個維度,都不需要修改原有系統,符合開閉原則
橋接模式的劣勢:
- 橋接模式的引入會增加系統的理解與設計模式,由于聚合關聯關系建立在抽象層,要求開發者針對抽象進行設計與編程
- 橋接模式要求正確識別系統中兩個獨立變化的維度,因此其使用范圍具有一定的局限性
10.靜態代理模式
角色分析:
- 抽象角色:
- 真實角色:
- 代理角色:
- 客戶:訪問代理物件的人
上代碼分析-1:
- 介面
public interface Rent{
public void rent();
}
- 真實角色(更加純粹!)
//房東
public class Host implements Rent{
public void rent(){
System.out.println("房東出租房子,");
}
}
- 代理角色
//租客
public class Client{
public static void main(String[] args){
//房東要出租房子
Host host = new Host();
//直接跟房東租房
//host.rent();
//代理
Proxy proxy = new Proxy(host);
proxy.rent();//附帶中介自己的附加操作
}
}
- 客戶端訪問代理角色
//中介(代理)
public class Proxy implements Rent{
private Host host;
public Proxy(){
}
public Proxy(Host host){
this.host = host;
}
public void rent(){
seeHouse();
sign();
host.rent();
fare();
}
//看房
public void seeHouse(){
System.out.println("中介帶你看房");
}
//簽合同
public void sign(){
System.out.println("簽租賃合同");
}
//收中介費
public void fare(){
System.out.println("收中介費");
}
}
代理模式的好處:
- 可以使真實角色的操作更加純粹(只需要專注于自己的業務,而不用在乎公共業務)
- 公共業務就交給代理角色,實作了業務的分工
- 公共業務發生擴展的時候,方便集中管理
代理模式的缺點:
- 一個真實角色就會產生一個代理角色:代碼量會翻倍,所以開發效率會變低
上代碼分析-2:
- 介面
//用戶需求
public interface UserService{
public void add();
public void delete();
public void update();
public void query();
}
- 真實角色(更加純粹!)
//真實物件
public class UserServiceImpl implements UserService{
public void add(){
//日志
//System.out.println("使用了add()方法");
System.out.println("增加了一個用戶");
}
public void delete(){
System.out.println("洗掉了一個用戶");
}
public void update(){
System.out.println("修改了一個用戶");
}
public void query(){
System.out.println("查詢了一個用戶");
}
}
- 代理角色
//代理(日志記錄)
public class UserServiceProxy implements UserService{
private UserServiceImpl userService;
public void setUserService(UserServiceImpl userService){
this.userService = userService;
}
public void add(){
log(msg:"add");
userService.add();
}
public void delete(){
log(msg:"delete");
userService.delete();
}
public void update(){
log(msg:"update");
userService.update();
}
public void query(){
log(msg:"query");
userService.query();
}
public void log(String msg){
System.out.println("[Debug] 使用了" + msg + "方法");
}
}
- 客戶端訪問代理角色
//客戶端
public class Client{
public static void main(String[] args){
UserServiceImpl userService = new UserServiceImpl();
UserServiceProxy proxy = new UserServiceProxy();
proxy.setUserService(userService);
proxy.add();
proxy.update();
}
}
11.動態代理模式
角色分析
- 抽象角色:
- 真實角色:
- 代理角色:
- 客戶:訪問代理物件的人
動態代理的代理類是動態生成的,不是我們直接寫好的
動態代理分為兩大類:
- 基于介面的動態代理√
JDK的動態代理- 基于類的動態代理
cglib- java位元組碼實作
javasist
需要了解兩個類:(自己去康康檔案8!)
- Proxy類
- InvocationHandler類
代碼演示
- 介面
public interface Rent{
public void rent();
}
- 真實角色(更加純粹!)
//房東
public class Host implements Rent{
public void rent(){
System.out.println("房東出租房子,");
}
}
- 代理角色
//這個類用來自動生成代理類
public class ProxyInvocationHandler implements InvocationHandler{
//被代理的介面
private void setRent(Rent rent){
this.rent = rent;
}
//生成得到代理類
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),h:this);
}
//處理代理實體,并回傳結果
public Object invoke(){
//動態代理的本質,就是使用反射機制實作
Object result = method.invoke(rent,args);
return result;
}
}
- 客戶端訪問代理角色
public class Client{
public static void main(String[] args){
//真實角色
Host host = new Host();
//代理角色:暫時沒有
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//通過呼叫程式處理角色來處理我們要呼叫的介面物件
pih.setRent(host);
Rent proxy = (Rent)pih.getProxy();
proxy.rent();
}
}
動態代理的好處:
- 可以使真實角色的操作更加純粹(只需要專注于自己的業務,而不用在乎公共業務)
- 公共業務就交給代理角色,實作了業務的分工
- 公共業務發生擴展的時候,方便集中管理
- 一個動態代理類代理的是一個介面,一般就是對應一類業務
- 一個動態代理類可以代理多個類,只要是實作了同一個介面即可
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/116228.html
標籤:設計模式
