結構性設計模式
針對類與物件的組織結構,(白話:類與物件之間的互動的多種模式
類/物件配接器模式
當需要傳入一個A型別引數,但只有B型別類時,就需要一個A型別的配接器裝入B類的資料,來將B資料轉成A型別,然后作為引數傳入
配接器,在生活中又稱轉換器,現在的手機基本都割去了3.5mm的耳機介面,此時只有有線耳機,要聽歌就需要一個轉換器將3.5mm介面轉成手機有的type-c的介面
類配接器(不建議)
繼承需要轉變的類
//主方法
public class Main {
public static void main(String[] args) {
TestSupplier supplier = new TestSupplier();
test( ? ); //我們沒有35MM型別的手機介面,只有type-c的手機介面,那這里該填個type-c,所以需要一個轉介面將35MM轉為type-c介面
}
public static void test(typeC typec){ //現在我們需要呼叫test方法,但是test方法需要型別是typeC
System.out.println("成功得到:"+typec.listen());
}
}
//介面
public interface typeC { //typeC介面也想聽歌
String listen();
}
//父類
public class 35MM{
public String listenMusic(){
return "有線耳機聽歌!" //因為只有有線耳機,所以只有35MM才能聽歌
}
}
//子類作配接器 繼承35MM,實作type-C介面
public class Adapter extends 35MM implements typeC{
@Override
public String listen() { //現在不再繼承35MM,僅實作typeC介面
return super.listenMusic();
}
}
物件配接器
將需要轉變的類實體化,并用作與配接器類的構造方法
因為類配接器會占用一個繼承位,而java又是單繼承的,如果typeC不是介面而是抽象類的話就用不了了,所以提出物件配接器:
//主方法
public class Main {
public static void main(String[] args) {
TestSupplier supplier = new TestSupplier();
test( ? ); //我們沒有35MM型別的手機介面,只有type-c的手機介面,那這里該填個type-c,所以需要一個轉介面將35MM轉為type-c介面
}
public static void test(typeC typec){ //現在我們需要呼叫test方法,但是test方法需要型別是typeC
System.out.println("成功得到:"+typec.listen());
}
}
//介面
public interface typeC { //typeC介面也想聽歌
String listen();
}
//父類
public class 35MM{
public String listenMusic(){
return "有線耳機聽歌!" //因為只有有線耳機,所以只有35MM才能聽歌
}
}
//子類作配接器 繼承35MM,實作type-C介面
public class Adapter implements typeC{ //現在不再繼承35MM,僅實作typeC介面
35MM 35mm; //實體化需要轉變的類
public Adapter(35MM 35mm){ //將實體化的物件用于構造物件
this.35mm = 35mm;
}
@Override
public String listen() { //接著實作listen方法,直接使用typeC提供的實作
return 35mm.listenMusic();
}
}
橋接模式
配置自定義
同一種產品有著不同的配置,就像手機有:運行記憶體 4 6 8g,存盤記憶體:64 128 256g,芯片:驍龍 A系列 麒麟 聯發科 獵戶座,不能每一種配置都寫一個類就太麻煩了,所以有了橋接模式,可以通過多個類橋接成一個產品類,
優勢:可以通過多個維度來自由設定配置
這里以華為手機舉例:(小知識——華為手機是用自家的麒麟芯片)
//第一層類:繼承該類可以自定義芯片型別
public abstract class AbstractPhone {
private Size size; //這里是描述存盤記憶體,由于舉例簡單點方便看得懂就不寫運行記憶體了
public AbstractPhone(Size size){
this.size = size;
}
public abstract String getType(); //這里的型別是指芯片型別
}
//介面及實作類
public interface Size{
String getSize();
}
public class 256G implements Size{
@Override
public String getSize() {
return "256g記憶體";
}
}
//第二層類:繼承該類可以自定義芯片型別和存盤記憶體的尺度大小
public abstract class RefinedAbstractPhone extends AbstractPhone{
protected RefinedAbstractPhone(Size size) {
super(size);
}
public String getSize(){ //添加尺寸維度獲取方式
return size.getSize();
}
}
//產品類:繼承第二層類,然后自定義存盤記憶體大小和芯片種類
public class HUAWEI extends RefinedAbstractPhone{
protected HUAWEI(Size size){ //構造方法指定具體存盤記憶體大小
super(size);
}
@Override
public String getType() {
return "華為手機"; //回傳手機品牌型別
}
}
//主方法
public static void main(String[] args) {
HUAWEI huawei = new HUAWEI(new 256G());
System.out.println(huawei.getType());
System.out.println(huawei.getSize());
}
組合模式
對多個組件進行統一一樣的操作
組合模式實際上就是將多個組件進行組合,讓用戶可以對它們進行一致性處理,比如我們的檔案夾,一個檔案夾中可以有很多個子檔案夾或是檔案,
它就像是一個樹形結構一樣,有分支有葉子,而組合模式則是可以對整個樹形結構上的所有節點進行遞回處理,比如我們現在希望將所有檔案夾中的檔案的名稱前面都添加一個前綴,那么就可以使用組合模式,

組合模式的示例如下,這里我們就用檔案和檔案夾的例子來講解:
/**
* 首先創建一個組件抽象,組件可以包含組件,組件有自己的業務方法
*/
public abstract class Component {
public abstract void addComponent(Component component); //添加子組件
public abstract void removeComponent(Component component); //洗掉子組件
public abstract Component getChild(int index); //獲取子組件
public abstract void test(); //執行對應的業務方法,比如修改檔案名稱
}
接著我們來撰寫兩種實作類:檔案夾實作類,檔案實作類
public class Directory extends Component{ //目錄可以包含多個檔案或目錄
List<Component> child = new ArrayList<>(); //這里我們使用List來存放目錄中的子組件
@Override
public void addComponent(Component component) {
child.add(component);
}
@Override
public void removeComponent(Component component) {
child.remove(component);
}
@Override
public Component getChild(int index) {
return child.get(index);
}
@Override
public void test() {
child.forEach(Component::test); //將繼續呼叫所有子組件的test方法執行業務
}
}
public class File extends Component{ //檔案就相當于是樹葉,無法再繼續添加子組件了
@Override
public void addComponent(Component component) {
throw new UnsupportedOperationException(); //不支持這些操作了
}
@Override
public void removeComponent(Component component) {
throw new UnsupportedOperationException();
}
@Override
public Component getChild(int index) {
throw new UnsupportedOperationException();
}
@Override
public void test() {
System.out.println("檔案名稱修改成功!"+this); //具體的名稱修改操作
}
}
最后,我們來測驗一下:可以看到我們對最外層目錄進行操作后,會遞回向下處理當前目錄和子目錄中所有的檔案
public static void main(String[] args) {
Directory outer = new Directory(); //新建一個外層目錄
Directory inner = new Directory(); //新建一個內層目錄
outer.addComponent(inner);
outer.addComponent(new File()); //在內層目錄和外層目錄都添加點檔案,注意別導錯包了
inner.addComponent(new File());
inner.addComponent(new File());
outer.test(); //開始執行檔案名稱修改操作
}
裝飾模式
通過B類 實作對A類方法執行前后,分別多執行一些操作,類似于AOP
適用:業務功能前后實作一些操作,如:在支付前提醒是否需要支付xxx元,
//頂層抽象類
public abstract class Base { //頂層抽象類,定義了一個test方法執行業務
public abstract void test();
}
//業務實作類
public class BaseImpl extends Base{
@Override
public void test() {
System.out.println("我是業務方法"); //具體的業務方法
}
}
//裝飾業務類(這里的構造方法引數是需要傳入實作業務類物件)
public class Decorator extends Base{ //裝飾者需要將裝飾目標組合到類中
protected Base base;
public Decorator(Base base) {
this.base = base;
}
@Override
public void test() {
base.test(); //這里暫時還是使用目標的原本方法實作
}
}
//具體實作裝飾業務類
public class DecoratorImpl extends Decorator{ //裝飾實作
public DecoratorImpl(Base base) {
super(base);
}
@Override
public void test() { //對原本的方法進行裝飾,我們可以在前后都去添加額外操作
System.out.println("裝飾方法:我是操作前邏輯");
super.test();
System.out.println("裝飾方法:我是操作后邏輯");
}
}
//主方法
public static void main(String[] args) {
Base base = new BaseImpl();
Decorator decorator = new DecoratorImpl(base); //將Base實作裝飾一下
Decorator outer = new DecoratorImpl(decorator); //裝飾者還可以嵌套,此時是裝飾兩次
decorator.test(); //裝飾一次:裝飾前——業務方法——裝飾后
outer.test(); //裝飾兩次:裝飾前——裝飾前——業務方法——裝飾后——裝飾后
}
代理模式
和裝飾模式代碼一模一樣,但核心是思想不同
裝飾模式和代理模式:
- 結構相同:都實作同一個介面/抽象類
- 作用不同:
- 裝飾器模式強調的是增強自身,在被裝飾之后你能夠在被增強的類上使用增強后的功能,增強后你還是你,只不過被強化了而已;
- 代理模式強調要讓別人幫你去做事情,以及添加一些本身與你業務沒有太多關系的事情(記錄日志、設定快取等)重點在于讓別人幫你做,
代理模式一般代碼:
//頂層抽象類
public abstract class Base { //頂層抽象類,定義了一個test方法執行業務
public abstract void test();
}
//業務實作類
public class BaseImpl extends Base{
@Override
public void test() {
System.out.println("我是業務方法"); //具體的業務方法
}
}
//代理業務類(這里的構造方法引數是需要傳入實作業務類物件)
public class Decorator extends Base{ //代理者需要將代理目標組合到類中
protected Base base;
public Decorator(Base base) {
this.base = base;
}
@Override
public void test() {
base.test(); //這里暫時還是使用目標的原本方法實作
}
}
//具體實作代理業務類
public class DecoratorImpl extends Decorator{ //代理實作
public DecoratorImpl(Base base) {
super(base);
}
@Override
public void test() { //對原本的方法進行代理,我們可以在前后都去添加額外操作
System.out.println("裝飾方法:我是操作前邏輯");
super.test();
System.out.println("裝飾方法:我是操作后邏輯");
}
}
//主方法
public static void main(String[] args) {
Base base = new BaseImpl();
Decorator decorator = new DecoratorImpl(base); //將Base實作代理一下
Decorator outer = new DecoratorImpl(decorator); //代理者還可以嵌套,此時是代理兩次
decorator.test(); //代理一次:代理前——業務方法——代理后
outer.test(); //代理兩次:代理前——代理前——業務方法——代理后——代理后
}
實作代理模式除了和裝飾模式一樣的代碼情況外還有兩種實作方式:【因為都是動態代理所以生成的代理類是看不到的】
-
JDK提供的動態代理:我們不再需要手動撰寫繼承關系創建代理類,它能夠在運行時通過反射機制為我們自動生成代理類:【只能代理介面】
//介面 public interface Subject { //JDK提供的動態代理只支持介面 void test(); } //介面實作類 public class SubjectImpl implements Subject{ @Override public void test() { System.out.println("我是測驗方法!"); } } //創建動態代理的處理邏輯(就是執行業務前后的方法撰寫在里面) public class TestProxy implements InvocationHandler { //代理類,需要實作InvocationHandler介面 private final Object object; //這里需要保存一下被代理的物件,下面需要用到 public TestProxy(Object object) { this.object = object; } @Override //此方法就是呼叫代理物件的對應方法時會進入,這里我們就需要撰寫如何進行代理了 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //method就是呼叫的代理物件的哪一個方法,args是實引陣列 System.out.println("代理的物件:"+proxy.getClass()); //proxy就是生成的代理物件了,我們看看是什么型別的 Object res = method.invoke(object, args); //在代理中呼叫被代理物件原本的方法,因為你是代理,還是得執行一下別人的業務,當然也可以不執行,但是這樣就失去代理的意義了,注意要用上面的object System.out.println("方法呼叫完成,回傳值為:"+res); //看看回傳值是什么 return res; //回傳回傳值 } } -
Spring在使用的CGLib框架代理,
maven依賴:
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.1</version> </dependency>代碼實作:
//介面 public interface Subject { //JDK提供的動態代理只支持介面 void test(); } //介面實作類 public class SubjectImpl implements Subject{ @Override public void test() { System.out.println("我是測驗方法!"); } } //創建動態代理的處理邏輯(就是執行業務前后的方法撰寫在里面) public class TestProxy implements MethodInterceptor { //首先還是撰寫我們的代理邏輯 private final Object target; //這些和之前JDK動態代理寫法是一樣的 public TestProxy(Object target) { this.target = target; } @Override //我們也是需要在這里去撰寫我們的代理邏輯 public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("現在是由CGLib進行代理操作!"+o.getClass()); return method.invoke(target, objects); //也是直接呼叫代理物件的方法即可 } } //主方法 public static void main(String[] args) { SubjectImpl subject = new SubjectImpl(); Enhancer enhancer = new Enhancer(); //增強器,一會就需要依靠增強器來為我們生成動態代理物件 enhancer.setSuperclass(SubjectImpl.class); //直接選擇我們需要代理的型別,直接不需要介面或是抽象類,SuperClass作為代理類的父類存在,這樣我們就可以按照指定型別的方式去操作代理類了 enhancer.setCallback(new TestProxy(subject)); //設定我們剛剛撰寫好的代理邏輯 SubjectImpl proxy = (SubjectImpl) enhancer.create(); //直接創建代理類 proxy.test(); //呼叫代理類的test方法 }
外觀模式
可以理解為門面模式,將需要通過操作多個類實作的一個功能封裝到一個類中,便于使用
當每個功能是一個系統,完成一個業務需要多個功能時就需要分別呼叫多個系統,此時就可以將一個業務需要使用的多個系統封裝成一個門面系統,只要呼叫該門面系統即可完成該業務,

舉例:比如現在我們設計了三個子系統,分別是排隊、結婚、領證,正常情況下我們是需要分別去找這三個部門去完成的,但是現在我們通過門面統一來完成
//系統一
public class SubSystemA {
public void test1(){
System.out.println("排隊");
}
}
//系統二
public class SubSystemB {
public void test2(){
System.out.println("結婚");
}
}
//系統三
public class SubSystemC {
public void test3(){
System.out.println("領證");
}
}
//門面
public class Facade {
SubSystemA a = new SubSystemA();
SubSystemB b = new SubSystemB();
SubSystemC c = new SubSystemC();
public void marry(){ //紅白喜事一條龍服務
a.test1();
b.test2();
c.test3();
}
}
//主方法
public static void main(String[] args) {
Facade facade = new Facade();
facade.marry();
}
享元模式
核心是共享,當A類方法里寫了一個方法,B類中需要同樣的方法就可以直接創建A類物件來呼叫方法或者通過一個方法工廠類收集各個方法,然后B類通過工廠類呼叫A類方法
舉例:通過享元工廠類實作共享方法
//A類
public class DBUtil {
public void selectDB(){
System.out.println("我是資料庫操作...");
}
}
//享元工廠
public class DBUtilFactory {
private static final DBUtil UTIL = new DBUtil(); //享元物件被存放在工廠中
public static DBUtil getFlyweight(){ //獲取享元物件
return UTIL;
}
}
//B類
public class UserService { //用戶服務
public void service(){
DBUtil util = DBUtilFactory.getFlyweight(); //通過享元工廠拿到DBUtil物件
util.selectDB(); //該干嘛干嘛
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/502041.html
標籤:設計模式
上一篇:設計模式之享元模式
下一篇:設計模式——創建型設計模式
