溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

常用設計模式 Java 實現

發(fā)布時間:2020-08-21 12:02:45 來源:網絡 閱讀:423 作者:灰白世界 欄目:編程語言

設計模式分類

創(chuàng)建型模式,共五種:工廠方法模式、抽象工廠模式、單例模式、建造者模式、原型模式。

結構型模式,共七種:適配器模式、裝飾器模式、代理模式、外觀模式、橋接模式、組合模式、享元模式。

行為型模式,共十一種:策略模式、模板方法模式、觀察者模式、迭代子模式、責任鏈模式、命令模式、備忘錄模式、狀態(tài)模式、訪問者模式、中介者模式、解釋器模式

設計模式六大原則

  • 開閉原則:開閉原則就是說對擴展開放,對修改關閉
  • 里氏代換原則:任何基類可以出現的地方,子類一定可以出現,只有當衍生類可以替換掉基類,軟件單位的功能不受到影響時,基類才能真正被復用,而衍生類也能夠在基類的基礎上增加新的行為
  • 依賴倒轉原則:面對接口編程,依賴于抽象而不依賴于具體
  • 迪米特法則:一個實體應當盡量少的與其他實體之間發(fā)生相互作用,使得系統(tǒng)功能模塊相對獨立
  • 合成復用原則:原則是盡量使用合成、聚合的方式,而不是使用繼承

單例模式

常用設計模式 Java 實現

什么是單例?

保證一個類只會創(chuàng)建一個實例

應用場景:

  • Winodw 任務管理器
  • Windows 回收站
  • 網站的在線人數計數器
  • 應用程序的日志應用
  • Web 應用配置對象的讀取
  • 數據庫連接池
  • 線程池
  • 文件系統(tǒng)
  • HttpApplication

運行時動態(tài)獲取類信息

餓漢式

/**
 * 餓漢式:
 *  類初始化時,會立即加載對象,線程天生安全,效率高
 */
public class User {
    // 存在方法區(qū),不會回收
    private static User user = new User();
    private User() {
    }

    public User getInstance() {
        return user;
    }

懶漢式

/**
 * 懶漢式:
 *  類初始化時,不會真正創(chuàng)建對象,只有使用時才真正創(chuàng)建對象
 */
public class User {

    private static User user;

    private User() {
    }

    public static synchronized User getInstance() {
        if (user == null)
            user = new User();
        return user;
    }
}

靜態(tài)內部類

/**
 * 靜態(tài)內部類:
 *  兼顧了懶漢模式的內存優(yōu)化(使用時才初始化)以及餓漢模式的安全性(不會被反射***)
 */
public class User {
    private User(){

    }

    static class SingletonClassInstance{
        private static final User user = new User();
    }

    public static User getInstance() {
        return SingletonClassInstance.user;
    }
}

枚舉方式

/**
 * 枚舉:
 *  枚舉天生就是單例,從JVM提供保障單例,避免反射,缺點沒有延遲加載
 */
public class User {

    private User() {
    }

    public static User getInstance() {
        return SingletonUserEnum.INSTANCE.getInstance();
    }

    static enum SingletonUserEnum{
        INSTANCE;
        private User user;
        private SingletonUserEnum() {
            user = new User();
        }

        public User getInstance() {
            return this.user;
        }
    }
}

雙重檢驗鎖

/**
 * 雙重檢驗鎖:
 *  線程安全的單例模式
 */
public class User {
    private String userName;
    private volatile static User3 user3;

    private User(){

    }

    public User getInstance(){
        if (user == null) {
            synchronized (this) {
                if (user == null){
                    user = new User();
                }
            }
        }
        return user;
    }
}

如何防止反射漏洞***?

在類里面增加一個 flag,初始值為 false,創(chuàng)建對象后更改為 true,如果為 true,就拋出異常

如何選擇單例創(chuàng)建方式?

需要延遲加載,選擇靜態(tài)內部類、懶漢式

不需要延遲加載,選擇枚舉類、餓漢式

多線程應用首選雙重檢驗鎖

工廠模式

常用設計模式 Java 實現

什么是工廠模式?

實現了創(chuàng)建者和調用者分離,工廠模式分為簡單工廠、工廠方法、抽象工廠模式

應用場景:

  • 日志記錄器:記錄可能記錄到本地硬盤、系統(tǒng)事件、遠程服務器等,用戶可以選擇記錄日志到什么地方
  • 數據庫訪問,當用戶不知道最后系統(tǒng)采用哪一類數據庫,以及數據庫可能有變化時
  • 設計一個連接服務器的框架,需要三個協議,"POP3"、"IMAP"、"HTTP",可以把這三個作為產品類,共同實現一個接口

簡單工廠

/**
 * 簡單工廠相當于一個工廠有各種產品,用戶無需知道具體產品的名稱,只需要知道產品對應的參數即可
 * 缺點是類型過多不利于擴展維護 
 */
public class CarFactory {
    public static Car createrCar(String name) {
        if (name == null || name.equals(""))
            return null;
        if (name.equals("比亞迪"))
            return new BydCar();
        if (name.equals("吉利"))
            return new JiliCar();
        return null;
    }
}

工廠方法

/**
 * 核心工廠不在負責所有產品的創(chuàng)建,而是將具體實現交給子類
 */
public interface CarFactory {
    Car createrCar(String name);
}

class BydFactory implements CarFactory {

    @Override
    public Car createrCar(String name) {
        return new BydCar();
    }
}

class JiliFactory implements CarFactory {
    @Override
    public Car createrCar(String name) {
        return new JiliCar();
    }
}

抽象工廠模式

常用設計模式 Java 實現

什么是抽象工廠模式?

抽象工廠模式(Abstract Factory Pattern)是圍繞一個超級工廠創(chuàng)建其他工廠

該超級工廠又稱為其他工廠的工廠,這種類型的設計模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對象的最佳方式

在抽象工廠模式中,接口是負責創(chuàng)建一個相關對象的工廠,不需要顯式指定它們的類,每個生成的工廠都能按照工廠模式提供對象

應用場景:

  • QQ 換皮膚,一整套一起換
  • 生成不同操作系統(tǒng)的程序
/**
 * 抽象工廠簡單地說是工廠的工廠,抽象工廠可以創(chuàng)建具體工廠,由具體工廠來產生具體產品
 */
public interface Engine {
    void run();
}

class EngineA implements Engine{

    @Override
    public void run() {
        System.out.println("發(fā)動機轉速快");
    }
}

class EngineB implements Engine {

    @Override
    public void run() {
        System.out.println("發(fā)動機轉速慢");
    }
}

public interface Chair {
    void run();
}

class ChairA implements Chair {

    @Override
    public void run() {
        System.out.println("自動加熱");
    }
}

class ChairB implements Chair {

    @Override
    public void run() {
        System.out.println("不能加熱");
    }
}

public interface CarFactory {
    // 創(chuàng)建發(fā)動機
    Engine createEngine();
    // 創(chuàng)建座椅
    Chair createChair();
}

public class JiliFactory implements CarFactory{

    @Override
    public Engine createEngine() {
        return new EngineA();
    }

    @Override
    public Chair createChair() {
        return new ChairA();
    }
}

簡單工廠、工廠方法、抽象工廠小結

  • 簡單工廠:用來生產同一等級結構中的任意產品
  • 工廠方法 :用來生產同一等級結構中的固定產品
  • 抽象工廠 :用來生產不同產品族的全部產品

代理模式

常用設計模式 Java 實現

什么是代理模式?

代理模式可以控制對象的訪問之前之后做一些操作,即 AOP

應用場景:

SpringAOP、事物原理、日志打印、權限控制、遠程調用、安全代理

靜態(tài)代理

/**
 * 由程序員創(chuàng)建或工具生成代理類的源碼,再編譯代理類
 * 所謂靜態(tài)也就是在程序運行前就已經存在代理類的字節(jié)碼文件,代理類和委托類的關系在運行前就確定了
 */
public interface IUserDao {
    void save();
}
public class UserDao implements IUserDao {
    public void save() {
        System.out.println("已經保存數據...");
    }
}
代理類
public class UserDaoProxy implements IUserDao {
    private IUserDao target;

    public UserDaoProxy(IUserDao iuserDao) {
        this.target = iuserDao;
    }

    public void save() {
        System.out.println("開啟事物...");
        target.save();
        System.out.println("提交事物...");
    }
}

jdk 動態(tài)代理

/**
 * 是根據類加載器和接口創(chuàng)建代理類
 * 通過實現InvocationHandler接口創(chuàng)建自己的調用處理器
 * 通過為Proxy類指定ClassLoader對象和一組interface創(chuàng)建動態(tài)代理類
 * 通過反射機制獲取動態(tài)代理類的構造函數
 * 通過構造函數創(chuàng)建代理類實例
 * 缺點必須提供接口
 */ 
public class InvocationHandlerImpl implements InvocationHandler {
    // 目標對象
    private Object target;

    public InvocationHandlerImpl(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("開啟事務");
        // 返回執(zhí)行結果
        Object invoke = method.invoke(target, args);
        System.out.println("提交事務");
        return invoke;
    }

    public static void main(String[] args) {
        IUserDao userDao = new UserDaoImpl();
        InvocationHandlerImpl invocationHandler = new InvocationHandlerImpl(userDao);
        // 獲得類加載器
        ClassLoader classLoader = userDao.getClass().getClassLoader();
        // 獲得接口
        Class<?>[] interfaces = userDao.getClass().getInterfaces();
        IUserDao o = (IUserDao) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
        o.add();
    }
}

cglib 動態(tài)代理

/**
 * 利用asm開源包,對代理對象類的class文件加載進來,通過修改其字節(jié)碼生成子類來處理
 * 不需要提供接口
 */
public class CglibAutoProxy implements MethodInterceptor {
    // 目標對象
    private Object target;
    public Object getInstance(Object target){
        this.target = target;
        // 操作字節(jié)碼生成虛擬子類
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("開啟事務");
        Object invoke = methodProxy.invoke(target, objects);
        System.out.println("提交事務");
        return invoke;
    }

    public static void main(String[] args) {

        CglibAutoProxy cglibAutoProxy = new CglibAutoProxy();
        UserDaoImpl o = (UserDaoImpl) cglibAutoProxy.getInstance(new UserDaoImpl());
        o.add();
    }
}

CGLIB 動態(tài)代理與 JDK 動態(tài)區(qū)別?

JDK 動態(tài)代理是利用反射機制生成一個實現代理接口的匿名類,在調用具體方法前調用 InvokeHandler 來處理
而 CGLIB 動態(tài)代理是利用 asm 開源包,對代理對象類的 class 文件加載進來,通過修改其字節(jié)碼生成子類來處理

Spring 中:

如果目標對象實現了接口,默認情況下會采用 JDK 的動態(tài)代理實現 AOP

如果目標對象實現了接口,可以強制使用 CGLIB 實現 AOP

如果目標對象沒有實現了接口,必須采用 CGLIB 庫,spring 會自動在 JDK 動態(tài)代理和 CGLIB 之間轉換

JDK 動態(tài)代理只能對實現了接口的類生成代理,而不能針對類 。

CGLIB 是針對類實現代理,主要是對指定的類生成一個子類,覆蓋其中的方法

因為是繼承,所以該類或方法最好不要聲明成 final ,final 可以阻止繼承和多態(tài)

建造者模式

常用設計模式 Java 實現

什么是建造者模式?

是將一個復雜的對象的構建與它的表示分離,使得同樣的構建過程可以創(chuàng)建不同的表示

工廠模式提供的是創(chuàng)建單個類的模式,而建造者模式則是將各種產品集中起來管理,用來創(chuàng)建復合對象

建造者模式包含以下幾個角色:

Builder:給出一個抽象接口,以規(guī)范產品對象的各個組成成分的建造。這個接口規(guī)定要實現復雜對象的哪些部分的創(chuàng)建,并不涉及具體的對象部件的創(chuàng)建

ConcreteBuilder:實現Builder接口,針對不同的商業(yè)邏輯,具體化復雜對象的各部分的創(chuàng)建

Director:調用具體建造者來創(chuàng)建復雜對象的各個部分,在指導者中不涉及具體產品的信息,只負責保證對象各部分完整創(chuàng)建或按某種順序創(chuàng)建

Product:要創(chuàng)建的復雜對象

應用場景:

  • 去肯德基,漢堡、可樂、薯條、炸雞翅等是不變的,而其組合是經常變化的,生成出所謂的"套餐"
  • JAVA 中的 StringBuilder
public class Person {
    private String head;
    private String body;
    private String foot;
    // 省略get和set   
}

public interface PersonBuilder {
     void builderHead();
     void builderBody();
     void builderFoot();
     Person builderPerson();
}

public class ManBuilder implements PersonBuilder {
    private Person person;
    public ManBuilder(){
        person = new Person();
    }
    public void builderHead() {
        person.setHead("美國人頭部 鼻子尖、長臉、藍眼睛");
    }

    public void builderBody() {
        person.setBody("美國人 長得比較高、塊頭大");
    }

    public void builderFoot() {
        person.setFoot("美國人 腿長");
    }

    public Person builderPerson() {
        return person;
    }
}

public class PersonDirector {
    public Person creater(PersonBuilder personBuilder) {
        personBuilder.builderHead();
        personBuilder.builderBody();
        personBuilder.builderFoot();
        return personBuilder.builderPerson();
    }

    public static void main(String[] args) {
        PersonDirector personDirector = new PersonDirector();
        Person creater = personDirector.creater(new ManBuilder());
        System.out.println(creater);
    }
}

模板模式

常用設計模式 Java 實現

什么是模板模式?

完成一件事情,有固定的數個步驟,但是每個步驟根據對象的不同,而實現細節(jié)不同;就可以在父類中定義一個完成該事情的總方法,按照完成事件需要的步驟去調用其每個步驟的實現方法,每個步驟的具體實現,由子類完成

應用場景:

  • 在造房子的時候,地基、走線、水管都一樣,只有在建筑的后期才有加壁櫥加柵欄等差異
  • 西游記里面菩薩定好的 81 難,這就是一個頂層的邏輯骨架
  • spring 中對 Hibernate 的支持,將一些已經定好的方法封裝起來,比如開啟事務、獲取 Session、關閉 Session 等,程序員不重復寫那些已經規(guī)范好的代碼,直接丟一個實體就可以保存
// 短信模板
public abstract class MsgTemplate {
    public void sendMsg() {
        addHeadLog();
        httpRequest();
        addHeadLog();
    }

    // 開始日志
    void addHeadLog() {
        System.out.println("調用運營商開始。。。");
    }

    abstract void httpRequest();

    void addFootLog() {
        System.out.println("調用運營商結束。。。");
    }
}

public class Liantong extends MsgTemplate {
    void httpRequest() {
        System.out.println("聯通。。。");
    }
}

// 具體實現細節(jié)
public class Yidong extends MsgTemplate {
    void httpRequest() {
        System.out.println("移動。。。");
    }
}

public class ClientTemplate {
    public static void main(String[] args) {
        Yidong yidong = new Yidong();
        yidong.sendMsg();
    }
}

適配器模式

常用設計模式 Java 實現

什么是適配器模式

將一個類的接口轉換成客戶希望的另一個接口,適配器模式讓那些接口不兼容的類可以一起工作

應用場景:

  • 美國電器 110V,中國 220V,就要有一個適配器將 110V 轉化為 220V
  • 在 LINUX 上運行 WINDOWS 程序
  • JAVA 中的 jdbc
public interface JP110VInterface {
    void connect();
}

public class JP110VInterfaceImpl implements JP110VInterface {
    public void connect() {
        System.out.println("日本110V電源接口");
    }
}

public interface ZN220VInterface {
    public void connect();
}

public class ZN220VInterfaceImpl implements ZN220VInterface {
    public void connect() {
        System.out.println("中國220V電源接口");
    }
}

public class ElectricCooker {
    private JP110VInterface jp110VInterface;
    public ElectricCooker(JP110VInterface jp110VInterface) {
        this.jp110VInterface = jp110VInterface;
    }
    public void cook(){
        jp110VInterface.connect();
        System.out.println("開啟做飯");
    }
}

public class PowerAdpater implements JP110VInterface {
    private ZN220VInterface zn220VInterface;
    public PowerAdpater(ZN220VInterface zn220VInterface) {
        this.zn220VInterface = zn220VInterface;
    }
    public void connect() {
        zn220VInterface.connect();
    }

    public static void main(String[] args) {
        ZN220VInterface zn220VInterface = new ZN220VInterfaceImpl();
        PowerAdpater powerAdpater = new PowerAdpater(zn220VInterface);
        ElectricCooker electricCooker = new ElectricCooker(powerAdpater);
        electricCooker.cook();
    }
}

外觀模式

什么是外觀模式?

外觀模式門面模式,隱藏系統(tǒng)的復雜性,并向客戶端提供了一個客戶端可以訪問系統(tǒng)的接口

應用場景:

  • 去醫(yī)院看病,可能要去掛號、門診、劃價、取藥,讓患者或患者家屬覺得很復雜,如果有提供接待人員,只讓接待人員來處理,就很方便
  • JAVA 的三層開發(fā)模式
// 微信推送消息
public interface WeiXinSmsService {
    public void sendSms();  
}

public class WeiXinSmsServiceImpl implements WeiXinSmsService{
    public void sendSms() {
        System.out.println("發(fā)送微信消息");

    }
}

// 發(fā)送郵件
public interface EamilSmsService {
      public void sendSms();    
}

public class EamilSmsServiceImpl implements EamilSmsService{

    @Override
    public void sendSms() {
        System.out.println("發(fā)送郵件消息");
    }
}

// 支付寶推送消息
public interface AliSmsService {
    public void sendSms();
}

public class AliSmsServiceImpl implements AliSmsService {

    @Override
    public void sendSms() {
        System.out.println("支付寶發(fā)送消息...");
    }
}

public class Computer {
    AliSmsService aliSmsService;
    EamilSmsService eamilSmsService;
    WeiXinSmsService weiXinSmsService;
    public Computer() {
        aliSmsService = new AliSmsServiceImpl();
        eamilSmsService = new EamilSmsServiceImpl();
        weiXinSmsService = new WeiXinSmsServiceImpl();
    }

    public void sendMsg() {
        aliSmsService.sendSms();
        eamilSmsService.sendSms();
        weiXinSmsService.sendSms();
    }
}

public class Client {

    public static void main(String[] args) {
        new Computer().sendMsg();
    }
}

原型模式

常用設計模式 Java 實現

什么是原型模式?

原型模式可以用來克隆對象,被復制的實例就是原型,多用于創(chuàng)建復雜的實例,更節(jié)約性能。

應用場景:

  • 資源優(yōu)化場景
  • 類初始化需要消化非常多的資源,這個資源包括數據、硬件資源等
  • 性能和安全要求的場景
  • 通過 new 產生一個對象需要非常繁瑣的數據準備或訪問權限,則可以使用原型模式
  • 一個對象多個修改者的場景
  • 一個對象需要提供給其他對象訪問,而且各個調用者可能都需要修改其值時,可以考慮使用原型模式拷貝多個對象供調用者使用
  • 在實際項目中,原型模式很少單獨出現,一般是和工廠方法模式一起出現,通過 clone 的方法創(chuàng)建一個對象,然后由工廠方法提供給調用者

淺復制和深復制:

只拷貝基本數據類型的數據,對于引用類型數據,僅復制引用,也就是說原型和復制后的對象共享一個變量,如果引用地址的數據發(fā)生改變,那么復制出來的對象也會發(fā)生改變,我們成為淺復制

深復制是在內存中開辟一塊空間存放復制對象

public class Book implements Cloneable{
    private String title;
    private ArrayList<String> listImg = new ArrayList<String>();
    public Book() {
        super();
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public List<String> getListImg() {
        return listImg;
    }

    public void setListImg(ArrayList<String> listImg) {
        this.listImg = listImg;
    }

    public void addImage(String img) {
        this.listImg.add(img);
    }

    public void showBook() {
        System.out.println("----------------start--------------");
        System.out.println("title:" + title);
        for (String img:listImg) {
            System.out.println("img:" + img);
        }
        System.out.println("-----------------end---------------");
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Book book = (Book) super.clone();
        book.listImg=(ArrayList<String>)this.listImg.clone();
        return book;
    }
}

public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        Book book = new Book();
        book.setTitle("圖書1");
        book.addImage("第一章");
        book.showBook();
        Book book1 = (Book) book.clone();
        book1.addImage("第二章");
        book1.showBook();
        book.showBook();
    }
}

策略模式

常用設計模式 Java 實現

什么是策略模式?

定義了一系列的算法,并將每一個算法封裝起來,而且使它們還可以相互替換。策略模式讓算法獨立于使用它的客戶而獨立變化

應用場景:

  • 諸葛亮的錦囊妙計,每一個錦囊就是一個策略
  • 旅行的出游方式,選擇騎自行車、坐汽車,每一種旅行方式都是一個策略
package com.kernel.strategy;

public interface Strategy {
    void algorithmInterface();
}

class StrategyA implements Strategy{

    public void algorithmInterface() {
        System.out.println("級別一處理邏輯");
    }
}

class StrategyB implements Strategy{

    public void algorithmInterface() {
        System.out.println("級別二處理邏輯");
    }
}

class StrategyC implements Strategy{

    public void algorithmInterface() {
        System.out.println("級別三處理邏輯");
    }
}

class Context{
    private Strategy strategy;
    Context(Strategy strategy) {
        this.strategy = strategy;
    }

    void algorithmInterface() {
        strategy.algorithmInterface();
    }
}

class Client {
    public static void main(String[] args) {
        Strategy strategyA = new StrategyA();
        Context context = new Context(strategyA);
        context.algorithmInterface();
        context = new Context(new StrategyB());
        context.algorithmInterface();
        context = new Context(new StrategyC());
        context.algorithmInterface();
    }
}
向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI