1. 適配器模式*
適配器模式:將一個(gè)類的接口轉(zhuǎn)換成客戶希望的另外一個(gè)接口,使得原本由于接口不兼容而不能一起工作的那些類可以一起工作
SpringMVC中的HandlerAdapter就使用了適配器模式
1.1 類適配器模式
// 客戶希望使用的接口:destination
interface MyStack {
void pop();
}
// 被適配者:source
class MyList {
public void removeFirst() {
System.out.println("名叫removeFirst的出棧");
}
}
// 類適配器:通過繼承Source類励稳、實(shí)現(xiàn)Destination接口窍株,完成Source->Destination的適配
class Adapter extends MyList implements MyStack {
@Override
public void pop() {
this.removeFirst();
}
}
public class AdapterClient {
public static void main(String[] args) {
MyStack stack = new Adapter();
stack.pop();
}
}
1.2 對象適配器模式
// 客戶希望使用的接口:destination
interface MyStack {
void pop();
}
// 被適配者:source
class MyList {
public void removeFirst() {
System.out.println("名叫removeFirst的出棧");
}
}
/**
* 對象適配器*:通過復(fù)合晕换,除了滿足“用戶期待接口”筐喳,還降低了代碼間的不良耦合
* 通過復(fù)合Source類法绵、實(shí)現(xiàn)Destination接口转质,完成Source->Destination的適配
*/
class Adapter implements MyStack {
private MyList myList = new MyList();
@Override
public void pop() {
myList.removeFirst();
}
}
public class AdapterClient {
public static void main(String[] args) {
MyStack stack = new Adapter();
stack.pop();
}
}
1.3 缺省適配器模式
// 客戶希望使用的接口:destination
interface ListStack {
void pop();
void removeFirst();
}
/**
* 缺省適配器:當(dāng)不需要使用一個(gè)接口的全部方法時(shí)佩抹,
* 可以設(shè)計(jì)一個(gè)抽象類實(shí)現(xiàn)接口的所有方法叼风,但有的方法可以做成空方法,
* 那么該抽象類的子類可以選擇性的重寫父類的某些方法
*/
abstract class Adapter implements ListStack {
@Override
public void pop() {
}
@Override
public void removeFirst() {
}
}
// 被適配者:source
class MyList extends Adapter {
@Override
public void removeFirst() {
System.out.println("我只需要removeFirst()方法");
}
}
public class AdapterClient {
public static void main(String[] args) {
ListStack list = new MyList();
list.removeFirst();
// Source類也可以用匿名內(nèi)部類來代替
ListStack stack = new Adapter() {
@Override
public void pop() {
System.out.println("我只需要pop()方法");
}
};
stack.pop();
}
}
2. 橋接模式
橋接模式:將抽象部分與它的實(shí)現(xiàn)部分分離棍苹,使它們可以獨(dú)立地變化无宿,又稱為柄體模式或接口模式
橋接模式模式基于單一職責(zé)原則,通過使用封裝枢里、聚合及繼承等方式讓不同的類承擔(dān)不同的職責(zé)
橋梁模式所涉及的角色:
- 實(shí)現(xiàn)化角色(Implementor):這個(gè)角色給出實(shí)現(xiàn)化角色的接口
- 具體實(shí)現(xiàn)化角色(Concrete Implementor):這個(gè)角色給出實(shí)現(xiàn)化角色接口的具體實(shí)現(xiàn)
- 抽象化角色(Abstraction):抽象化給出的定義孽鸡,并保存一個(gè)對實(shí)現(xiàn)化對象的引用
- 修正抽象化角色(Refined Abstraction):抽象化角色的子類蹂午,改變和修正父類對抽象化的定義
實(shí)現(xiàn)化角色應(yīng)當(dāng)只給出底層操作,而抽象化角色應(yīng)當(dāng)只給出基于底層操作的更高一層的操作
// 實(shí)現(xiàn)化角色:顏色
interface Color {
void showColor();
}
// 具體實(shí)現(xiàn)化角色:黑色
class Black implements Color {
@Override
public void showColor() {
System.out.print("黑色的");
}
}
// 具體實(shí)現(xiàn)化角色:白色
class White implements Color {
@Override
public void showColor() {
System.out.print("白色的");
}
}
// 抽象化角色:在內(nèi)部聚合一個(gè)實(shí)現(xiàn)化角色
abstract class Animal {
Color color;
public void show() {
this.color.showColor();
}
}
// 修正抽象化角色:狗
class Dog extends Animal {
@Override
public void show() {
super.show();
System.out.println("狗汪汪叫");
}
}
// 修正抽象化角色:貓
class Cat extends Animal {
@Override
public void show() {
super.show();
System.out.println("貓喵喵叫");
}
}
public class BridgeClient {
public static void main(String[] args) {
Dog dog = new Dog();
dog.color = new Black();
// 黑色的狗汪汪叫
dog.show();
Cat cat = new Cat();
cat.color = new White();
// 白色的貓喵喵叫
cat.show();
}
}
3. 裝飾模式*
裝飾模式:在不必改變原類文件和使用繼承的情況下彬碱,動態(tài)地給一個(gè)對象添加一些額外的職責(zé)豆胸;它是通過創(chuàng)建一個(gè)包裝對象,也就是裝飾來包裹真實(shí)的對象巷疼。
裝飾模式特點(diǎn):
- 裝飾對象和真實(shí)對象有相同的接口
- 裝飾對象包含一個(gè)真實(shí)對象的引用
- 裝飾對象接受所有來自客戶端的請求晚胡,裝飾對象可以在轉(zhuǎn)發(fā)這些請求以前或以后增加一些附加功能
裝飾模式所涉及的角色:
- 抽象構(gòu)件角色(Component):給出一個(gè)抽象接口,以規(guī)范準(zhǔn)備接收附加責(zé)任的對象
- 具體構(gòu)件角色(Concrete Component):定義一個(gè)將要接收附加責(zé)任的類
- 裝飾角色(Decorator):持有一個(gè)構(gòu)件(Component)對象的實(shí)例嚼沿,并實(shí)現(xiàn)一個(gè)與抽象構(gòu)件接口一致的接口
- 具體裝飾角色(Concrete Decorator):負(fù)責(zé)給構(gòu)件對象添加上附加的責(zé)任
裝飾模式簡化:
- 如果只有一個(gè)具體構(gòu)件角色而沒有抽象構(gòu)件角色時(shí)估盘,可以讓裝飾角色繼承具體構(gòu)件角色
- 如果只有一個(gè)具體裝飾角色時(shí),可以將裝飾角色和具體裝飾角色合并
裝飾模式適用情況:
- 需要擴(kuò)展一個(gè)類的功能骡尽,或給一個(gè)類添加附加職責(zé)
- 需要動態(tài)的給一個(gè)對象添加功能遣妥,這些功能可以再動態(tài)的撤銷
- 需要增加由一些基本功能的排列組合而產(chǎn)生的非常大量的功能
- 當(dāng)不能采用生成子類的方法進(jìn)行擴(kuò)充時(shí)
Java的IO流就采用了裝飾模式
裝飾者模式原理類圖:
// 抽象構(gòu)件角色
interface IO {
void copy();
}
// 具體構(gòu)件角色
class MyIO implements IO {
@Override
public void copy() {
System.out.println("拷貝");
}
}
// 裝飾角色
class Decorator implements IO {
IO io;
public Decorator(IO io) {
this.io = io;
}
@Override
public void copy() {
io.copy();
}
}
// 具體裝飾角色:BufferedIO
class BufferedIO extends Decorator {
public BufferedIO(IO io) {
super(io);
}
@Override
public void copy() {
super.copy();
addBuffered();
}
public void addBuffered() {
System.out.println("擁有緩沖功能");
}
}
// 具體裝飾角色:LineNumberIO
class LineNumberIO extends Decorator {
public LineNumberIO(IO io) {
super(io);
}
@Override
public void copy() {
super.copy();
addLineNumber();
}
public void addLineNumber() {
System.out.println("擁有操作行號功能");
}
}
public class DecoratorClient {
public static void main(String[] args) {
IO io = new Decorator(new MyIO());
io = new BufferedIO(io);
io = new LineNumberIO(io);
// 調(diào)用過程類似遞歸,一層一層裝飾
io.copy();
/**結(jié)果:
* 拷貝
* 擁有緩沖功能
* 擁有操作行號功能
*/
}
}
4. 組合模式
組合模式:將對象組合成樹形結(jié)構(gòu)以表示“部分-整體”的層次結(jié)構(gòu)爆阶。Composite使得用戶對單個(gè)對象和組合對象的使用具有一致性燥透。
組合模式所涉及的角色:
- Component:是組合中的對象聲明接口沙咏,在適當(dāng)?shù)那闆r下辨图,實(shí)現(xiàn)所有類共有接口的默認(rèn)行為。聲明一個(gè)接口用于訪問和管理Component子部件
- Leaf:在組合中表示葉子結(jié)點(diǎn)對象
- Composite:定義有枝節(jié)點(diǎn)行為肢藐,用來存儲子部件故河,在Component接口中實(shí)現(xiàn)與子部件有關(guān)操作,如增加(add)和刪除(remove)等
java.util.HashMap就使用了組合模式
import java.util.ArrayList;
import java.util.List;
abstract class Component {
String name;
public Component(String name) {
this.name = name;
}
void add(Component component) {
throw new UnsupportedOperationException();
}
void remove(Component component) {
throw new UnsupportedOperationException();
}
abstract void display(int depth);
}
class Composite extends Component {
List<Component> componentList = new ArrayList<>();
public Composite(String name) {
super(name);
}
@Override
public void add(Component component) {
componentList.add(component);
}
@Override
public void remove(Component component) {
componentList.remove(component);
}
@Override
public void display(int depth) {
StringBuilder sb = new StringBuilder("");
for (int i = 0; i < depth; i++) {
sb.append("-");
}
System.out.println(new String(sb) + this.name);
for (Component component : componentList) {
component.display(depth + 3);
}
}
}
class Leaf extends Component {
public Leaf(String name) {
super(name);
}
@Override
public void display(int depth) {
StringBuilder sb = new StringBuilder("");
for (int i = 0; i < depth; i++) {
sb.append("-");
}
System.out.println(new String(sb) + this.name);
}
}
public class CompositeClient {
public static void main(String[] args) {
Composite root = new Composite("數(shù)計(jì)學(xué)院");
Composite jsj = new Composite("計(jì)算機(jī)專業(yè)");
Composite wl = new Composite("網(wǎng)絡(luò)專業(yè)");
jsj.add(new Leaf("設(shè)計(jì)模式"));
jsj.add(new Leaf("數(shù)據(jù)結(jié)構(gòu)"));
wl.add(new Leaf("網(wǎng)絡(luò)原理"));
root.add(jsj);
root.add(wl);
root.display(0);
/**結(jié)果:類似DOM樹
* 數(shù)計(jì)學(xué)院
* ---計(jì)算機(jī)專業(yè)
* ------設(shè)計(jì)模式
* ------數(shù)據(jù)結(jié)構(gòu)
* ---網(wǎng)絡(luò)專業(yè)
* ------網(wǎng)絡(luò)原理
*/
}
}
5. 外觀模式
外觀模式(過程模式):為子系統(tǒng)中的一組接口提供一個(gè)一致的界面吆豹,F(xiàn)acade模式定義了一個(gè)高層接口鱼的,這個(gè)接口使得子系統(tǒng)更加容易使用
org.apache.ibatis.session.Configuration類的newMetaObject方法就使用了外觀模式
// 子系統(tǒng):CPU
class CPU {
public void freeze() {
}
public void jump() {
}
public void execute() {
}
}
// 子系統(tǒng):Memory
class Memory {
public void load() {
}
}
// 外觀類:為子系統(tǒng)提供一個(gè)共同的對外接口
class Computer {
CPU cpu = new CPU();
Memory memory = new Memory();
public void startComputer() {
cpu.freeze();
memory.load();
cpu.jump();
cpu.execute();
}
}
// 客戶端:通過一個(gè)外觀接口讀寫子系統(tǒng)中各接口的數(shù)據(jù)資源
public class FacadeClient {
public static void main(String[] args) {
Computer computer = new Computer();
computer.startComputer();
}
}
6. 享元模式
享元模式:運(yùn)用共享技術(shù)有效地支持大量細(xì)粒度的對象。使用共享物件來盡可能減少內(nèi)存使用量以及分享資訊給盡可能多的相似物件
享元模式的兩個(gè)狀態(tài):
- 內(nèi)蘊(yùn)狀態(tài)存儲在享元內(nèi)部痘煤,不會隨環(huán)境的改變而有所不同凑阶,是可以共享的
- 外蘊(yùn)狀態(tài)是不可以共享的,它隨環(huán)境的改變而改變的衷快,因此外蘊(yùn)狀態(tài)是由客戶端來保持
享元模式所涉及的角色:
- 抽象享元角色(FlyWeight):是具體享元的抽象宙橱,同時(shí)定義出對象的內(nèi)蘊(yùn)狀態(tài)和外蘊(yùn)狀態(tài)的接口或?qū)崿F(xiàn)
- 具體享元角色(ConcreteFlyWeight):實(shí)現(xiàn)抽象角色規(guī)定的方法。如果存在內(nèi)蘊(yùn)狀態(tài)蘸拔,就負(fù)責(zé)為內(nèi)蘊(yùn)狀態(tài)提供存儲空間
- 享元工廠角色(FlyWeightFactory):用于構(gòu)建一個(gè)池容器师郑,同時(shí)提供從池中獲取對象方法;這個(gè)角色的實(shí)現(xiàn)是共享的關(guān)鍵
- 客戶端角色(Client):維護(hù)對所有享元對象的引用调窍,而且還需要存儲對應(yīng)的外蘊(yùn)狀態(tài)
享元模式能夠解決重復(fù)對象的內(nèi)存浪費(fèi)的問題宝冕,享元模式的經(jīng)典應(yīng)用場景就是池技術(shù)
java.lang.Integer類的valueOf方法就采用了享元模式
import java.util.HashMap;
// 外蘊(yùn)狀態(tài)
class OuterStatus {
String outerState;
public OuterStatus(String outerState) {
this.outerState = outerState;
}
}
// 抽象享元角色
interface Flyweight {
void use(OuterStatus outerStatus);
}
// 具體享元角色
class ConcreteFlyWeight implements Flyweight {
// 共享部分,內(nèi)蘊(yùn)狀態(tài)
String type;
public ConcreteFlyWeight(String type) {
this.type = type;
}
@Override
public void use(OuterStatus outerStatus) {
System.out.println("具體享元:" + type + "\t外蘊(yùn)狀態(tài):" + outerStatus.outerState);
}
}
// 享元工廠角色
class FlyWeightFactory {
HashMap<String, Flyweight> pool = new HashMap<>();
public Flyweight getFlyweight(String type) {
if (!pool.containsKey(type)) {
pool.put(type, new ConcreteFlyWeight(type));
}
return pool.get(type);
}
public int getFlyweightSize() {
return pool.size();
}
}
// 客戶端角色
public class FlyweightClient {
public static void main(String[] args) {
FlyWeightFactory flyWeightFactory = new FlyWeightFactory();
Flyweight sqlPool1 = flyWeightFactory.getFlyweight("數(shù)據(jù)庫連接池");
sqlPool1.use(new OuterStatus("url"));
Flyweight sqlPool2 = flyWeightFactory.getFlyweight("數(shù)據(jù)庫連接池");
sqlPool2.use(new OuterStatus("driver"));
int flyweightSize = flyWeightFactory.getFlyweightSize();
System.out.println(flyweightSize);
}
}
7. 代理模式*
代理模式概念:為其他對象提供一種代理以控制對這個(gè)對象的訪問邓萨。在某些情況下地梨,一個(gè)對象不適合或者不能直接引用另一個(gè)對象菊卷,而代理對象可以在客戶端和目標(biāo)對象之間起到中介的作用
代理模式組成:
- 抽象角色:通過接口或抽象類聲明真實(shí)角色實(shí)現(xiàn)的業(yè)務(wù)方法
- 真實(shí)角色:實(shí)現(xiàn)抽象角色,定義真實(shí)角色所要實(shí)現(xiàn)的業(yè)務(wù)邏輯湿刽,供代理角色調(diào)用
- 代理角色:實(shí)現(xiàn)抽象角色的烁,是真實(shí)角色的代理,通過真實(shí)角色的業(yè)務(wù)邏輯方法來實(shí)現(xiàn)抽象方法诈闺,并可以附加自己的操作
7.1 靜態(tài)代理
靜態(tài)代理是在程序運(yùn)行前就已經(jīng)存在代理類的字節(jié)碼文件渴庆,代理類和委托類的關(guān)系在運(yùn)行前就確定了
真實(shí)角色和代理角色實(shí)現(xiàn)同一個(gè)接口
// 抽象角色
interface MyConnection {
void createStatement();
void close();
}
// 真實(shí)角色
class JdbcConnection implements MyConnection {
@Override
public void createStatement() {
System.out.println("獲取執(zhí)行SQL的對象");
}
@Override
public void close() {
System.out.println("關(guān)閉連接");
}
}
// 代理角色
class ProxyConnection implements MyConnection {
JdbcConnection jdbcConnection = new JdbcConnection();
@Override
public void createStatement() {
jdbcConnection.createStatement();
}
@Override
public void close() {
System.out.println("歸還連接");
}
}
public class ProxyClient {
public static void main(String[] args) {
MyConnection connection = new ProxyConnection();
connection.createStatement();
connection.close();
}
}
7.2 JDK動態(tài)代理
動態(tài)代理是在實(shí)現(xiàn)階段不用關(guān)心代理類,而在運(yùn)行階段才指定哪一個(gè)對象
JDK動態(tài)代理是基于接口的動態(tài)代理:要求被代理類至少實(shí)現(xiàn)一個(gè)接口
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 抽象角色
interface MyConnection {
void createStatement();
void close();
}
// 真實(shí)角色
class JdbcConnection implements MyConnection {
@Override
public void createStatement() {
System.out.println("獲取執(zhí)行SQL的對象");
}
@Override
public void close() {
System.out.println("關(guān)閉連接");
}
}
// JDK動態(tài)代理
class JDKProxyConnectionFactory {
MyConnection connection = new JdbcConnection();
// 動態(tài)生成代理角色
public MyConnection getProxyInstance() {
return (MyConnection) Proxy.newProxyInstance(JdbcConnection.class.getClassLoader(), new Class[]{MyConnection.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object object = null;
// 如果是close方法雅镊,就增強(qiáng)該方法
if ("close".equals(method.getName())) {
System.out.println("歸還連接");
} else {
// 調(diào)用真實(shí)角色的方法
object = method.invoke(connection, args);
}
return object;
}
});
}
}
public class ProxyClient {
public static void main(String[] args) {
JDKProxyConnectionFactory jdkProxyConnectionFactory = new JDKProxyConnectionFactory();
MyConnection connection = jdkProxyConnectionFactory.getProxyInstance();
connection.createStatement();
connection.close();
}
}
7.3 CGLib動態(tài)代理
CGLib(Code Generation Library)比java.lang.reflect.Proxy類更強(qiáng)的在于它不僅可以接管接口類的方法襟雷,還可以接管普通類的方法
CGLib動態(tài)代理是基于子類的動態(tài)代理:被代理類可以不必實(shí)現(xiàn)接口,它是在內(nèi)存中構(gòu)建一個(gè)子類對象從而實(shí)現(xiàn)對目標(biāo)對象功能擴(kuò)展仁烹,因此真實(shí)角色類不能被final修飾
CGLib的底層是通過使用Java字節(jié)碼操作框架ASM來轉(zhuǎn)換字節(jié)碼并生成新的類
使用CGLib需導(dǎo)入的jar包耸弄,如:aspectjweaver-1.8.7.jar(Spring的AOP依賴)或cglib-2.2.2.jar+asm-3.3.1.jar
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
// 真實(shí)角色
class JdbcConnection {
public void createStatement() {
System.out.println("獲取執(zhí)行SQL的對象");
}
public void close() {
System.out.println("關(guān)閉連接");
}
}
// CGLib動態(tài)代理
class CGLibProxyConnectionFactory {
JdbcConnection jdbcConnection = new JdbcConnection();
// 獲取一個(gè)代理角色
public JdbcConnection getProxyInstance() {
// 1. 創(chuàng)建Enhancer對象
Enhancer enhancer = new Enhancer();
// 2. 設(shè)置父類(要代理的類)
enhancer.setSuperclass(jdbcConnection.getClass());
// 3. 設(shè)置回調(diào)函數(shù)
enhancer.setCallback(new MethodInterceptor() {
/**
* 參數(shù)說明:
* Object:表示要進(jìn)行增強(qiáng)的對象
* Method:表示攔截的方法
* Object[]:表示參數(shù)列表,基本數(shù)據(jù)類型需要傳入其包裝類型
* MethodProxy:表示對方法的代理
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object object = null;
// 如果是close方法卓缰,就增強(qiáng)該方法
if ("close".equals(method.getName())) {
System.out.println("歸還連接");
} else {
// 調(diào)用真實(shí)角色的方法
object = method.invoke(jdbcConnection, args);
System.out.println("---------------------");
// 調(diào)用真實(shí)角色的方法
methodProxy.invokeSuper(obj, args);
}
return object;
}
});
// 4. 創(chuàng)建代理對象
return (JdbcConnection) enhancer.create();
}
}
public class ProxyClient {
public static void main(String[] args) {
CGLibProxyConnectionFactory cgLibProxyConnectionFactory = new CGLibProxyConnectionFactory();
JdbcConnection jdbcConnection = cgLibProxyConnectionFactory.getProxyInstance();
jdbcConnection.createStatement();
jdbcConnection.close();
}
}