當(dāng)前位置:首頁(yè) > 公眾號(hào)精選 > C語言與CPP編程
[導(dǎo)讀]每日一句英語學(xué)習(xí),每天進(jìn)步一點(diǎn)點(diǎn): "Without purpose, the days would have ended, as such days always end, in disintegration." 「少了目標(biāo),一天還是會(huì)結(jié)束,它總是以支離破碎的形式結(jié)束。」 前言 羊哥之前寫一篇有趣的文章《答應(yīng)我,別再if/else走天下

<section>
每日一句英語學(xué)習(xí),每天進(jìn)步一點(diǎn)點(diǎn):
  • "Without purpose, the days would have ended, as such days always end, in disintegration."

  • 「少了目標(biāo),一天還是會(huì)結(jié)束,它總是以支離破碎的形式結(jié)束。」


前言

羊哥之前寫一篇有趣的文章《答應(yīng)我,別再if/else走天下了可以嗎 | CodeSheep 》,在文中使用 Java 語言實(shí)現(xiàn)了枚舉類、工廠模式和策略模式的三種方式,來消除連環(huán)的if/else。內(nèi)容層層遞進(jìn),由淺入深的方式我非常喜歡。

看到有留言中有小伙伴想看 C++ 版本的,特此寫下了此文(已經(jīng)過羊哥的同意)。不過由于 C++ 沒有枚舉類,所以本文不涉及此方式,但本文會(huì)帶大家一步一步的優(yōu)化工廠模式策略模式。

正文

糟糕 if / else 連環(huán)

if/else可以說是我們學(xué)習(xí)編程時(shí),第一個(gè)學(xué)習(xí)的分支語句,簡(jiǎn)單易理解,生活中也處處有的if/else例子:

老婆給當(dāng)程序員的老公打電話:“下班順路買一斤包子帶回來,如果看到賣西瓜的,買一個(gè)。”  
當(dāng)晚,程序員老公手捧一個(gè)包子進(jìn)了家門。。。
老婆怒道:“你怎么就買了一個(gè)包子?!”
老公答曰:“因?yàn)榭吹搅速u西瓜的。”

老婆的思維:

買一斤包子;
if( 看到賣西瓜的 )
  買一只( 西瓜 );

而程序員老公的程序:

if( ! 看見賣西瓜的 ) 
   買一斤包子;
else
   買一只( 包子 );

非常生生動(dòng)動(dòng)的生活例子!如果身為程序員的你,犯了同樣的思維錯(cuò)誤,別繼續(xù)問你媳婦為什么,問就是跪鍵盤

進(jìn)入本文正題。考慮以下栗子:一般來說我們正常的后臺(tái)管理系統(tǒng)都有所謂的角色的概念,不同管理員權(quán)限不一樣,能夠行使的操作也不一樣。

  • 系統(tǒng)管理員(ROLE_ROOT_ADMIN):有A操作權(quán)限

  • 訂單管理員(ROLE_ORDER_ADMIN):有B操作權(quán)限

  • 普通用戶(ROLE_NORMAL):有C操作權(quán)限

假設(shè)一個(gè)用戶進(jìn)來,我們需要根據(jù)不同用戶的角色來判斷其有哪些行為。使用過多if/else連環(huán)寫法的我們,肯定下意識(shí)就覺得,這不簡(jiǎn)單嘛,我上演一套連環(huán)的寫法:

class JudgeRole
{

public:
    std::string Judgestd::string roleName )
    
{
        std::string result = "";
        if( roleName == "ROLE_ROOT_ADMIN" )       // 系統(tǒng)管理員
        {
            result = roleName + "has A permission";
        }
        else if( roleName == "ROLE_ORDER_ADMIN" ) // 訂單管理員
        {
            result = roleName + "has B permission";
        }
        else if( roleName == "ROLE_NORMAL" )       // 普通用戶
        {
            result = roleName + "has C permission";
        }
        return result;
    }
};

當(dāng)系統(tǒng)里有幾十個(gè)角色,那豈不是幾十個(gè)if/else嵌套,這個(gè)視覺效果絕對(duì)酸爽……這種實(shí)現(xiàn)方式非常的不優(yōu)雅。

別人看了這種代碼肯定大聲喊:“我X,哪個(gè)水貨寫的!”

這時(shí)你聽到,千萬不要說:“那我改成switch/case”。千萬別說,千萬別說哦,否則可能拎包回家了……

因?yàn)?code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(248, 35, 117);background: rgb(248, 248, 248);">switch/case和if/else毛區(qū)別都沒,都是寫費(fèi)勁、難閱讀、不易擴(kuò)展的代碼

接下來簡(jiǎn)單講幾種改進(jìn)方式,別再 if / else 走天下了。


工廠模式 —— 它不香嗎?

不同的角色做不同的事情,很明顯就提供了使用工廠模式的契機(jī),我們只需要將不同情況單獨(dú)定義好,并聚合到工廠里面即可。

首先,定義一個(gè)公用接口RoleOperation,類里有一個(gè)純虛函數(shù)Op,供派生類(子類)具體實(shí)現(xiàn):

// 基類
class RoleOperation
{

public:
    virtual std::string Op() 0// 純虛函數(shù)
    virtual ~RoleOperation() {} // 虛析構(gòu)函數(shù)
};

接下來針對(duì)不同的角色類,繼承基類,并實(shí)現(xiàn) Op 函數(shù):

// 系統(tǒng)管理員(有 A 操作權(quán)限)
class RootAdminRole : public RoleOperation {
public:
    RootAdminRole(const std::string &roleName)
            : m_RoleName(roleName) {}

    std::string Op() {
        return m_RoleName + " has A permission";
    }

private:
    std::string m_RoleName;
};


// 訂單管理員(有 B 操作權(quán)限)
class OrderAdminRole : public RoleOperation {
public:
    OrderAdminRole(const std::string &roleName)
            : m_RoleName(roleName) {}

    std::string Op() {
        return m_RoleName + " has B permission";
    }

private:
    std::string m_RoleName;
};

// 普通用戶(有 C 操作權(quán)限)
class NormalRole : public RoleOperation {
public:
    NormalRole(const std::string &roleName)
            : m_RoleName(roleName) {}

    std::string Op() {
        return m_RoleName + " has C permission";
    }

private:
    std::string m_RoleName;
};

接下來在寫一個(gè)工廠類RoleFactory,提供兩個(gè)接口:

  • 用以注冊(cè)角色指針對(duì)象到工廠的RegisterRole成員函數(shù);

  • 用以獲取對(duì)應(yīng)角色指針對(duì)象的GetRole成員函數(shù)。


// 角色工廠
class RoleFactory {
public:
    // 獲取工廠單例,工廠的實(shí)例是唯一的
    static RoleFactory& Instance() {
        static RoleFactory instance; // C++11 以上線程安全
        return instance;
    }

    // 把指針對(duì)象注冊(cè)到工廠
    void RegisterRole(const std::string& name, RoleOperation* registrar) {
        m_RoleRegistry[name] = registrar;
    }

    // 根據(jù)名字name,獲取對(duì)應(yīng)的角色指針對(duì)象
    RoleOperation* GetRole(const std::string& name) {

        std::map<std::string, RoleOperation*>::iterator it;

        // 從map找到已經(jīng)注冊(cè)過的角色,并返回角色指針對(duì)象
        it = m_RoleRegistry.find(name);
        if (it != m_RoleRegistry.end()) {
            return it->second;
        }

        return nullptr// 未注冊(cè)該角色,則返回空指針
    }

private:
    // 禁止外部構(gòu)造和虛構(gòu)
    RoleFactory() {}
    ~RoleFactory() {}

    // 禁止外部拷貝和賦值操作
    RoleFactory(const RoleFactory &);
    const RoleFactory &operator=(const RoleFactory &);

    // 保存注冊(cè)過的角色,key:角色名稱 , value:角色指針對(duì)象
    std::map<std::string, RoleOperation *> m_RoleRegistry;
};

把所有的角色注冊(cè)(聚合)到工廠里,并封裝成角色初始化函InitializeRole

void InitializeRole() // 初始化角色到工廠
{
    static bool bInitialized = false;

    if (bInitialized == false) {
        // 注冊(cè)系統(tǒng)管理員
        RoleFactory::Instance().RegisterRole("ROLE_ROOT_ADMIN"new RootAdminRole("ROLE_ROOT_ADMIN"));
        // 注冊(cè)訂單管理員
        RoleFactory::Instance().RegisterRole("ROLE_ORDER_ADMIN"new OrderAdminRole("ROLE_ORDER_ADMIN"));
        // 注冊(cè)普通用戶
        RoleFactory::Instance().RegisterRole("ROLE_NORMAL"new NormalRole("ROLE_NORMAL"));
        bInitialized = true;
    }
}

接下來借助上面這個(gè)工廠,業(yè)務(wù)代碼調(diào)用只需要一行代碼,if/else被消除的明明白白:

class JudgeRole {
public:
    std::string Judge(const std::string &roleName) {
        return RoleFactory::Instance().GetRole(roleName)->Op();
    }
};

需要注意:在使用Judge時(shí),要先調(diào)用初始化所有角色 InitializeRole 函數(shù)(可以放在main函數(shù)開頭等):

int main() {
    InitializeRole(); // 優(yōu)先初始化所有角色到工廠

    JudgeRole judgeRole;

    std::cout << judgeRole.Judge("ROLE_ROOT_ADMIN") << std::endl;
    std::cout << judgeRole.Judge("ROLE_ORDER_ADMIN") << std::endl;
    std::cout << judgeRole.Judge("ROLE_NORMAL") << std::endl;
}

通過工廠模式實(shí)現(xiàn)的方式,想擴(kuò)展條件也很容易,只需要增加新代碼,而不需要改動(dòng)以前的業(yè)務(wù)代碼,非常符合「開閉原則」。

不知道小伙伴發(fā)現(xiàn)了沒有,上面實(shí)現(xiàn)工廠類,雖然看來去井然有序,但是當(dāng)使用不當(dāng)時(shí)會(huì)招致程序奔潰,那么是什么情況會(huì)發(fā)生呢?

我們先來分析上面的工廠類對(duì)外的兩個(gè)接口:

  • RegisterRole注冊(cè)角色指針對(duì)象到工廠

  • GetRole從工廠獲取角色指針對(duì)象

難道是指針對(duì)象沒有釋放導(dǎo)致資源泄露?不,不是這個(gè)問題,我們也不必手動(dòng)去釋放指針,因?yàn)樯厦娴墓S是「單例模式」,它的生命周期是從第一次初始化后到程序結(jié)束,那么程序結(jié)束后,操作系統(tǒng)自然就會(huì)回收工廠類里的所有指針對(duì)象資源。

但是當(dāng)我們手動(dòng)去釋放從工廠獲取的角色指針對(duì)象,那么就會(huì)有問題了:

RoleOperation* pRoleOperation =  RoleFactory::Instance().GetRole(roleName);
...
delete pRoleOperation; // 手動(dòng)去釋放指針對(duì)象

如果我們手動(dòng)釋放了指針對(duì)象,也就導(dǎo)致工廠里 map 中存放的指針對(duì)象指向了,當(dāng)下次再次使用時(shí),就會(huì)招致程序奔潰!如下面的例子:

class JudgeRole {
public:
    std::string Judge(const std::string &roleName) {
        RoleOperation *pRoleOperation = RoleFactory::Instance().GetRole(roleName);
        std::string ret = pRoleOperation->Op();
        delete pRoleOperation; // 手動(dòng)去釋放指針對(duì)象
        return ret;
    }
};

int main() {
    InitializeRole(); // 優(yōu)先初始化所有角色到工廠

    JudgeRole judgeRole;

    std::cout << judgeRole.Judge("ROLE_ROOT_ADMIN") << std::endl;
    std::cout << judgeRole.Judge("ROLE_ROOT_ADMIN") << std::endl// 錯(cuò)誤!程序會(huì)奔潰退出!

    return 0;
}

上面的代碼在使用第二次ROLE_ROOT_ADMIN角色指針對(duì)象時(shí),就會(huì)招致程序奔潰,因?yàn)?code style="font-size: inherit;line-height: inherit;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(248, 35, 117);background: rgb(248, 248, 248);">ROLE_ROOT_ADMIN角色指針對(duì)象已經(jīng)在第一次使用完后,被手動(dòng)釋放指針對(duì)象了,此時(shí)工廠map存放的就是空指針了。

可否優(yōu)化呢?因?yàn)橛械某绦騿T是會(huì)手動(dòng)釋放從工廠獲取的指針對(duì)象的。

上面的工廠類的缺陷就在于,new初始化的指針對(duì)象只初始化了一次,如果手動(dòng) 釋放了指針對(duì)象,就會(huì)導(dǎo)致此指針對(duì)象指向空,再次使用就會(huì)導(dǎo)致系統(tǒng)奔潰。

為了改進(jìn)這個(gè)問題,那么我們把 new初始化方式放入工廠類獲取指針對(duì)象的成員函數(shù)里,這也就每次調(diào)用該成員函數(shù)時(shí),都是返回新new初始化過的指針對(duì)象,那么這時(shí)外部就需要由手動(dòng)釋放指針對(duì)象了。

下面的工廠類,改進(jìn)了上面問題,同時(shí)采用模板技術(shù),進(jìn)一步對(duì)工廠類進(jìn)行了封裝,使得不管是角色類,還是其他類,只要存在多態(tài)特性的類,都可以使用此工廠類,可以說是「萬能」的工廠類了:

「萬能」工廠

接下來把新的「萬能」工廠模板類,使用到本例的角色對(duì)象。

1. 把角色注冊(cè)(聚合)到工廠的方式是構(gòu)造ProductRegistrar對(duì)象 ,使用時(shí)需注意:

  • 模板參數(shù)ProductType_t指定的是基類(如本例RoleOperation

  • 模板參數(shù)ProductImpl_t指定的是派生類(如本例 RootAdminRole、OrderAdminRole 和 NormalRole

我們使用新的注冊(cè)(聚合)方式,對(duì)InitializeRole初始化角色函數(shù)改進(jìn)下,參見下面:

void InitializeRole() // 初始化角色到工廠
{
    static bool bInitialized = false;

    if (bInitialized == false) {
        // 注冊(cè)系統(tǒng)管理員
        static ProductRegistrar<RoleOperation, RootAdminRole> rootRegistrar("ROLE_ROOT_ADMIN");
        // 注冊(cè)訂單管理員
        static ProductRegistrar<RoleOperation, OrderAdminRole> orderRegistrar("ROLE_ORDER_ADMIN");
        // 注冊(cè)普通用戶
        static ProductRegistrar<RoleOperation, NormalRole> normalRegistrar("ROLE_NORMAL");
        bInitialized = true;
    }
}

2. 從工廠獲取角色指針對(duì)象的函數(shù)是GetProduct,需注意的是:

  • 使用完角色指針對(duì)象后,需手動(dòng)delete資源。

我們使用新的獲取角色對(duì)象的方式,對(duì)Judge函數(shù)改進(jìn)下,參見下面:

class JudgeRole {
public:
    std::string Judge(const std::string &roleName) {
        ProductFactory<RoleOperation>& factory = ProductFactory<RoleOperation>::Instance();
        // 從工廠獲取對(duì)應(yīng)的指針對(duì)象
        RoleOperation *pRoleOperation = factory.GetProduct(roleName);
        // 調(diào)用角色的對(duì)應(yīng)操作權(quán)限
        std::string result = pRoleOperation->Op();
        // 手動(dòng)釋放資源
        delete pRoleOperation;
        return result;
    }
};

唔,每次都手動(dòng)釋放資源這種事情,會(huì)很容易遺漏。如果我們遺漏了,就會(huì)招致了內(nèi)存泄漏。為了避免此概率事情的發(fā)生,我們用上「智能指針],讓它幫我們管理吧:

class JudgeRole {
public:
    std::string Judge(const std::string &roleName) {
        ProductFactory<RoleOperation>& factory = ProductFactory<RoleOperation>::Instance();
        std::shared_ptr<RoleOperation> pRoleOperation(factory.GetProduct(roleName));
        return pRoleOperation->Op();
    }
};

采用了std::shared_ptr引用計(jì)數(shù)智能指針,我們不在需要時(shí)刻記住要手動(dòng)釋放資源的事情啦(我們通常都會(huì)忘記……),該智能指針會(huì)在當(dāng)引用次數(shù)為 0 時(shí),自動(dòng)會(huì)釋放掉指針資源。

來,我們接著來,除了工廠模式,策略模式也不妨試一試


策略模式 —— 它不香嗎?

策略模式和工廠模式寫起來其實(shí)區(qū)別也不大!策略模式也采用了面向?qū)ο蟮睦^承和多態(tài)機(jī)制。

在上面工廠模式代碼的基礎(chǔ)上,按照策略模式的指導(dǎo)思想,我們也來創(chuàng)建一個(gè)所謂的策略上下文類,這里命名為RoleContext

class RoleContext {
public:
    RoleContext(RoleOperation *operation) : m_pOperation(operation) {
    }

    ~RoleContext() {
        if (m_pOperation) {
            delete m_pOperation;
        }
    }

    std::string execute() {
        return m_pOperation->Op();
    }

private:
    // 禁止外部拷貝和賦值操作
    RoleContext(const RoleContext &);
    const RoleContext &operator=(const RoleContext &);

    RoleOperation *m_pOperation;
};

很明顯上面?zhèn)魅氲膮?shù)operation就是表示不同的「策略」。我們?cè)跇I(yè)務(wù)代碼里傳入不同的角色,即可得到不同的操作結(jié)果:

class JudgeRole {
public:
    std::string Judge(RoleOperation *pOperation) {
        RoleContext roleContext(pOperation);
        return roleContext.execute();
    }
};

int main() {
    JudgeRole judgeRole;

    std::cout << judgeRole.Judge(new RootAdminRole("ROLE_ROOT_ADMIN")) << std::endl;
    std::cout << judgeRole.Judge(new OrderAdminRole("ROLE_ORDER_ADMIN")) << std::endl;
    std::cout << judgeRole.Judge(new NormalRole("ROLE_NORMAL")) << std::endl;

    return 0;
}

當(dāng)然,上面策略類還可以進(jìn)一步優(yōu)化:

  • 用模板技術(shù)進(jìn)一步封裝,使其不限制于角色類。

// 策略類模板
// 模板參數(shù) ProductType_t,表示的是基類
template <class ProductType_t>
class ProductContext {

public:
    ProductContext(ProductType_t *operation) 
                : m_pOperation(operation) {
    }

    ~ProductContext() {
        if (m_pOperation) {
            delete m_pOperation;
        }
    }

    std::string execute() {
        return m_pOperation->Op();
    }

private:
    // 禁止外部拷貝和賦值操作
    ProductContext(const ProductContext &);
    const ProductContext &operator=(const ProductContext &);

    ProductType_t* m_pOperation;
};

使用方式,沒太大差別,只需要指定類模板參數(shù)是基類(如本例 RoleOperation) 即可:

class JudgeRole {
public:
    std::string Judge(RoleOperation *pOperation) {
        ProductContext<RoleOperation> roleContext(pOperation);
        return roleContext.execute();
    }
};

共勉

C++ 和 Java 語言都是面向?qū)ο缶幊痰姆绞?,所以都是可以通過面向?qū)ο蠛投鄳B(tài)特性降低代碼的耦合性,同時(shí)也可使得代碼易擴(kuò)展。所以對(duì)于寫代碼事情,不要著急下手,先思考是否有更簡(jiǎn)單、更好的方式去實(shí)現(xiàn)。

C++ 之父 Bjarne Stroustrup 曾經(jīng)提及過程序員的三大美德是懶惰、急躁、傲慢,其中之一的懶惰這個(gè)品質(zhì),就是告知我們要花大力氣去思考,避免消耗過多的精力個(gè)體力(如敲代碼)。


section>

點(diǎn)【在看】是最大的支持 

免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問題,請(qǐng)聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

倫敦2024年8月29日 /美通社/ -- 英國(guó)汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時(shí)1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動(dòng) BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時(shí)企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對(duì)日本游戲市場(chǎng)的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)開幕式在貴陽(yáng)舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對(duì)環(huán)境變化,經(jīng)營(yíng)業(yè)績(jī)穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤(rùn)率延續(xù)升勢(shì) 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長(zhǎng) 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競(jìng)爭(zhēng)力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競(jìng)爭(zhēng)優(yōu)勢(shì)...

關(guān)鍵字: 通信 BSP 電信運(yùn)營(yíng)商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺(tái)與中國(guó)電影電視技術(shù)學(xué)會(huì)聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會(huì)上宣布正式成立。 活動(dòng)現(xiàn)場(chǎng) NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長(zhǎng)三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會(huì)上,軟通動(dòng)力信息技術(shù)(集團(tuán))股份有限公司(以下簡(jiǎn)稱"軟通動(dòng)力")與長(zhǎng)三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉