文章目錄
- 1. 設計模式的特點與簡介
- 1.1 什么是設計模式
- 1.2 Java中的設計模式
- 2. 工廠模式
- 2.1 簡單工廠模式
- 2.2 小結
- 3. 代理模式
- 3.1 動態代理
- 3.2 JDK動態代理
- 3.3 小結
- 4. 單例模式
- 4.1 懶漢式單例與餓漢式單例
- 4.2 小結
- 5. 策略模式
- 5.1 策略模式
- 5.2小結
1. 設計模式的特點與簡介
1.1 什么是設計模式
設計模式(Design pattern):代表了最佳的實踐,通常被有經驗的面向物件的軟體開發人員所采用,設計模式是軟體開發人員在軟體開發程序中面臨的一般問題的解決方案,這些解決方案是眾多軟體開發人員經過相當長的一段時間的試驗和錯誤總結出來的,
簡而言之:就是書寫代碼的一些規律,例如經常開車的人都知道,開車要用一檔起步
1.2 Java中的設計模式
java的設計模式大體上分為三大類:一共23種
- 創建型模式(
5種):工廠方法模式,抽象工廠模式,單例模式,建造者模式,原型模式, - 結構型模式(
7種):配接器模式,裝飾器模式,代理模式,外觀模式,橋接模式,組合模式,享元模式, - 行為型模式(
11種):策略模式、模板方法模式、觀察者模式、迭代子模式、責任鏈模式、命令模式、備忘錄模式、狀態模式、訪問者模式、中介者模式、解釋器模式,
設計模式遵循的原則有6個:
- 開閉原則(
Open Close Principle):對擴展開放,對修改關閉, - 里氏代換原則(
Liskov Substitution Principle):只有當衍生類可以替換掉基類,軟體單位的功能不受到影響時,基類才能真正被復用,而衍生類也能夠在基類的基礎上增加新的行為, - 依賴倒轉原則(
Dependence Inversion Principle):這個是開閉原則的基礎,對介面編程,依賴于抽象而不依賴于具體, - 介面隔離原則(
Interface Segregation Principle):使用多個隔離的介面來降低耦合度, - 迪米特法則(最少知道原則)(
Demeter Principle):一個物體應當盡量少的與其他物體之間發生相互作用,使得系統功能模塊相對獨立, - 合成復用原則(
Composite Reuse Principle)原則是盡量使用合成/聚合的方式,而不是使用繼承,繼承實際上破壞了類的封裝性,超類的方法可能會被子類修改,
2. 工廠模式
2.1 簡單工廠模式
定義一個用于創建物件的介面,讓子類決定實體化哪一個類,Factory Method使一個類的實體化延遲到其子類,
工廠方法即Factory Method,是一種物件創建型模式,
工廠方法的目的是使得創建物件和使用物件是分離的,并且客戶端總是參考抽象工廠和抽象產品:
┌─────────────┐ ┌─────────────┐
│ Product │ │ Factory │
└─────────────┘ └─────────────┘
▲ ▲
│ │
┌─────────────┐ ┌─────────────┐
│ ProductImpl │<─ ─ ─│ FactoryImpl │
└─────────────┘ └─────────────┘
舉例代碼:
車的介面
package com.rj.bd.lx.lx01;
public interface ICar {
//制造不同零件方法
public void make();
}
車介面的實作類
package com.rj.bd.lx.lx01;
/**
* @desc 發動機類
* @author 86186
*
*/
public class Engine implements ICar{
@Override
public void make() {
System.out.println("發動機制作完畢!!");
}
}
package com.rj.bd.lx.lx01;
/**
* @desc 底盤類
* @author 86186
*
*/
public class Chassis implements ICar{
@Override
public void make() {
System.out.println("底盤制作完畢!!");
}
}
package com.rj.bd.lx.lx01;
/**
* @desc 車身類
* @author 86186
*
*/
public class Body implements ICar{
@Override
public void make() {
System.out.println("車身制作完畢!!");
}
}
簡單的工廠類
package com.rj.bd.lx.lx01;
public class SimpleCarFactory {
public static final int TYPE_FDJ = 1;
public static final int TYPE_DP = 2;
public static final int TYPE_CS = 3;
public static ICar createNoodles(int type) {
switch(type) {
case TYPE_FDJ:
return new Engine();
case TYPE_DP:
return new Chassis();
case TYPE_CS:
return new Body();
default:
return new Body();//默認底盤類
}
}
}
測驗類
package com.rj.bd.lx.lx01;
public class Test {
public static void main(String[] args) {
ICar car01 = SimpleCarFactory.createNoodles(SimpleCarFactory.TYPE_FDJ);
car01.make();
ICar car02 = SimpleCarFactory.createNoodles(SimpleCarFactory.TYPE_DP);
car02.make();
ICar car03 = SimpleCarFactory.createNoodles(SimpleCarFactory.TYPE_CS);
car03.make();
System.out.println("全部制造完畢,起飛~");
}
}
測驗控制臺
發動機制作完畢!!
底盤制作完畢!!
車身制作完畢!!
全部制造完畢,起飛~
2.2 小結
- 工廠方法是指定義工廠介面和產品介面,但如何創建實際工廠和實際產品被推遲到子類實作,從而使呼叫方只和抽象工廠與抽象產品打交道,
- 實際更常用的是更簡單的靜態工廠方法,它允許工廠內部對創建產品進行優化,
- 呼叫方盡量持有介面或抽象類,避免持有具體型別的子類,以便工廠方法能隨時切換不同的子類回傳,卻不影響呼叫方代碼,
3. 代理模式
為其他物件提供一種代理以控制對這個物件的訪問,
暫不講述靜態代理模式
3.1 動態代理
相比靜態代理,動態代理具有更強的靈活性,因為它不用在我們設計實作的時候就指定某一個代理類來代理哪一個被代理物件,我們可以把這種指定延遲到程式運行時由JVM來實作
使用Java動態代理機制的好處:
- 減少編程的作業量:假如需要實作多種代理處理邏輯,只要寫多個代理處理器就可以了,無需每種方式都寫一個代理類,
- 功能的擴展:系統擴展性和維護性增強,程式修改起來也方便多了(一般只要改代理處理器類就行了),
動態代理的使用場景:
- 在后期的商業框架中很多都有用到的,例如
Spring,Struts2
3.2 JDK動態代理
JDK動態代理: JDK動態代理所用到的代理類在程式呼叫到代理類物件時才由JVM真正創建,JVM根據傳進來的 業務實作類物件 以及 方法名 ,動態地創建了一個代理類的class檔案并被位元組碼引擎執行,然后通過該代理類物件進行方法呼叫,我們需要做的,只需指定代理類的預處理、呼叫后操作即可
步驟:
- 首先,定義業務邏輯介面
- 然后,實作業務邏輯介面創建業務實作類
- 最后,實作 呼叫管理介面
InvocationHandler創建動態代理(工具)類 - 在使用時,首先創建一個業務實作類物件和一個代理類物件,然后定義介面參考(這里使用向上轉型)并用代理物件.
bind(業務實作類物件)的回傳值進行賦值,最后通過介面參考呼叫業務方法即可,(介面參考真正指向的是一個系結了業務類的代理類物件,所以通過介面方法名呼叫的是被代理的方法們)
代碼舉例
介面
package com.rj.bd.lx.lx03;
/**
* @desc 租房的介面
* @author 86186
*
*/
public interface IHouse {
public void renting();
}
租戶類
package com.rj.bd.lx.lx03;
/**
* @desc 租戶類
* @author 86186
*
*/
public class Tenant implements IHouse{
public String rentName;// 租戶名
public int rent;// 租金
public String getRentName() {
return rentName;
}
public void setRentName(String rentName) {
this.rentName = rentName;
}
public int getRent() {
return rent;
}
public void setRent(int rent) {
this.rent = rent;
}
@Override
public void renting() {
if (rent < 1000) {
System.out.println(rentName + "你這" + rent + "塊錢租不到房子呀,最少要1000");
return;
}
System.out.println(rentName + "租房花了" + rent);
}
}
動態代理工具類
package com.rj.bd.lx.lx03;
/**
* @desc 動態代理管理類
* @author 86186
*/
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DymaicProxy implements InvocationHandler {
public Object object;// 需要代理的物件
public DymaicProxy(Object object) {
this.object = object;// 代理租客
}
@Override
public Object invoke(Object arg0, Method arg1, Object[] arg2) throws Throwable {
Object resultObject = arg1.invoke(this.object, arg2);
return resultObject;
}
}
測驗JDK動態代理模式
package com.rj.bd.lx.lx03;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
/**
* @desc 3.用代理模式模擬一個房屋租賃
* @author 86186
*
*/
public class Test {
public static void main(String[] args) {
Tenant tenant = new Tenant();
tenant.setRentName("董夢宇");
tenant.setRent(3000);
InvocationHandler handler = new DymaicProxy(tenant);
IHouse house = (IHouse) Proxy.newProxyInstance(handler.getClass().getClassLoader(),
tenant.getClass().getInterfaces(), handler);
// System.out.println(handler);
house.renting();
Tenant tenant01 = new Tenant();
tenant01.setRentName("董大宇");
tenant01.setRent(500);
InvocationHandler handler01 = new DymaicProxy(tenant01);
IHouse house01 = (IHouse) Proxy.newProxyInstance(handler01.getClass().getClassLoader(),
tenant01.getClass().getInterfaces(), handler01);
// System.out.println(handler01);
house01.renting();
}
}
測驗控制臺
董夢宇租房花了3000
董大宇你這500塊錢租不到房子呀,最少要1000
3.3 小結
- 代理模式通過封裝一個已有介面,并向呼叫方回傳相同的介面型別,能讓呼叫方在不改變任何代碼的前提下增強某些功能(例如,鑒權、延遲加載、連接池復用等),
- 使用Proxy模式要求呼叫方持有介面,作為Proxy的類也必須實作相同的介面型別,
4. 單例模式
保證一個類僅有一個實體,并提供一個訪問它的全域訪問點,
單例模式(Singleton)的目的是為了保證在一個行程中,某個類有且僅有一個實體,
4.1 懶漢式單例與餓漢式單例
- 懶漢式:第一次呼叫的時候才初始化(執行緒不安全)
- 餓漢式:類加載的時候就初始化實體
接下來用一段代碼來表明懶漢式與餓漢式
懶漢式
package com.rj.bd.lx.lx02;
/**
* @desc 懶漢模式
* @author 86186
*
*/
public class Insurance {
private static Insurance insurance;
// 私有無參構造器
private Insurance() {
}
//初始化
public static Insurance getInsurance() {
if(insurance == null) {
insurance new Insurance();
}
return insurance;
}
}
餓漢式
package com.rj.bd.lx.lx02;
/**
* @desc 餓漢模式
* @author 86186
*
*/
public class Insurance {
private static Insurance insurance = new Insurance();
// 私有無參構造器
private Insurance() {
}
//初始化
public static Insurance getInsurance() {
return insurance;
}
}
其中懶漢模式是執行緒不安全的,可以使用synchronized關鍵字進行同步,但是會浪費很多時間,所以可以在最外面的一層加一個判斷,通過雙重判斷的方式(雙重加鎖機制),解決效率問題,減少鎖的判斷次數
如下:
package com.rj.bd.lx.lx02;
/**
* @desc 懶漢模式
* @author 86186
*
*/
public class Insurance {
private static Insurance insurance;
// 私有無參構造器
private Insurance() {
}
// 初始化
public static Insurance getInsurance() {
if (insurance == null) {
synchronized (Insurance.class) {
if (insurance == null) {
insurance = new Insurance();
}
}
}
return insurance;
}
}
代碼舉例
餓漢保單類
package com.rj.bd.lx.lx02;
/**
* @desc 餓漢模式
* @author 86186
*
*/
public class Insurance {
private static Insurance insurance = new Insurance();
// 私有無參構造器
private Insurance() {
}
//初始化
public static Insurance getInsurance() {
return insurance;
}
}
測驗類
package com.rj.bd.lx.lx02;
/**
* @desc 2.用單例模式模擬一下保險單號
* @author 86186
*
*/
public class Test {
public static void main(String[] args) {
// 模擬一個保險單,一個單只能有一個保險號,如果是其他的類使用,則會出現不同的保險單號,餓漢不會出現執行緒安全問題
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
@Override
public void run() {
Insurance onlyInsurance = Insurance.getInsurance();
System.out.println(onlyInsurance.hashCode());
}
}).start();
}
}
}
測驗控制臺
655160934
655160934
....
655160934
4.2 小結
Singleton模式是為了保證一個程式的運行期間,某個類有且只有一個全域唯一實體;Singleton模式既可以嚴格實作,也可以以約定的方式把普通類視作單例,
5. 策略模式
定義一系列的演算法,把它們一個個封裝起來,并且使它們可相互替換,本模式使得演算法可獨立于使用它的客戶而變化,
5.1 策略模式
策略模式:Strategy,是指,定義一組演算法,并把其封裝到一個物件中,然后在運行時,可以靈活的使用其中的一個演算法,
話不多說直接上差異代碼
不用策略模式:
package com.rj.bd.lx.lx04;
import java.security.Identity;
import java.util.Scanner;
/**
* @desc 模擬一個商場的商品打折促銷的小案例: 1)普通用戶原價,但是要是滿100元減去20元 2)普通會員是,打8折 0)VIP會員打7折
* @author 86186
*
*/
public class Test {
public static void main(String[] args) {
System.out.println("請輸入消費金額.....");
Scanner cin = new Scanner(System.in);
Double d = cin.nextDouble();
System.out.println("請輸入您的身份,普通用戶or普通會員orVIP會員:");
String identity = cin.next();
if (identity.equals("普通用戶")) {
if (d >= 100) {
System.out.println("您為普通用戶,消費為金額" + d + "滿減后為" + (d - 20));
return;
}
System.out.println("您為普通用戶,消費金額為" + d);
} else if (identity.equals("普通會員")) {
System.out.println("您為普通會員,消費金額為" + d + ",打8折后價格為" + (d * 0.8));
} else if (identity.equals("VIP會員")) {
System.out.println("您為VIP會員,消費金額為" + d + ",打7折后價格為" + (d * 0.7));
} else {
System.out.println("輸入錯誤!");
}
}
}
控制臺輸出
請輸入消費金額.....
150
請輸入您的身份,普通用戶or普通會員orVIP會員:
普通會員
您為普通會員,消費金額為150.0,打8折后價格為120.0
使用策略模式
策略介面
package com.rj.bd.lx.lx04;
/**
* @desc 策略介面
* @author 86186
*
*/
public interface IStrategy {
public void identity(double d);
}
普通用戶類
package com.rj.bd.lx.lx04;
/**
* @desc 普通用戶
* @author 86186
*
*/
public class OrdinaryUsers implements IStrategy{
@Override
public void identity(double d) {
if (d >= 100) {
System.out.println("您為普通用戶,消費為金額" + d + "滿減后為" + (d - 20));
return;
}
System.out.println("您為普通用戶,消費金額為" + d);
}
}
普通會員類
package com.rj.bd.lx.lx04;
/**
* @desc 普通會員
* @author 86186
*
*/
public class GeneralMember implements IStrategy{
@Override
public void identity(double d) {
System.out.println("您為普通會員,消費金額為" + d + ",打8折后價格為" + (d * 0.8));
}
}
VIP會員類
package com.rj.bd.lx.lx04;
/**
* @desc VIP會員
* @author 86186
*
*/
public class VIPMembers implements IStrategy{
@Override
public void identity(double d) {
System.out.println("您為VIP會員,消費金額為" + d + ",打7折后價格為" + (d * 0.7));
}
}
策略內容類
package com.rj.bd.lx.lx04;
/**
* @desc 策略內容類
* @author 86186
*
*/
public class StrategyContext {
public IStrategy strategy;
//策略內容的構造器
public StrategyContext(IStrategy strategy) {
super();
this.strategy = strategy;
}
//設定金額
public void consumptionAmount(double d) {
strategy.identity(d);
}
}
測驗類
package com.rj.bd.lx.lx04;
import java.util.Scanner;
/**
* @desc 4.用策略模式模擬一個商場的商品打折促銷的小案例:
1)普通用戶原價,但是要是滿100元減去20元
2)普通會員是,打8折
0)VIP會員打7折
* @author 86186
*
*/
public class Test {
public static void main(String[] args) {
System.out.println("請輸入消費金額.....");
Scanner cin=new Scanner(System.in);
Double d = cin.nextDouble();
System.out.print("普通用戶:");
StrategyContext strategyContext01 = new StrategyContext(new OrdinaryUsers());
strategyContext01.consumptionAmount(d);
System.out.print("會員用戶:");
StrategyContext strategyContext02 = new StrategyContext(new GeneralMember());
strategyContext02.consumptionAmount(d);
System.out.print("VIP會員:");
StrategyContext strategyContext03 = new StrategyContext(new VIPMembers());
strategyContext03.consumptionAmount(d);
}
}
測驗控制臺
請輸入消費金額.....
150
普通用戶:您為普通用戶,消費為金額150.0滿減后為130.0
會員用戶:您為普通會員,消費金額為150.0,打8折后價格為120.0
VIP會員:您為VIP會員,消費金額為150.0,打7折后價格為105.0
5.2小結
- 策略模式是為了允許呼叫方選擇一個演算法,從而通過不同策略實作不同的計算結果,
- 通過擴展策略,不必修改主邏輯,即可獲得新策略的結果,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/210417.html
標籤:其他
