欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android常用設(shè)計(jì)模式之原型模式詳解

 更新時(shí)間:2022年10月21日 11:05:22   作者:newki  
這篇文章主要為大家介紹了Android常用設(shè)計(jì)模式之原型模式詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前言

什么是原型模式?

它是指創(chuàng)建對(duì)象的種類,并通過(guò)拷貝這些原型創(chuàng)建新的對(duì)象。

它是用于創(chuàng)建重復(fù)的對(duì)象,同時(shí)又能保證性能。這種類型的設(shè)計(jì)模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對(duì)象的最佳方式。

這種模式是實(shí)現(xiàn)了一個(gè)原型接口,該接口用于創(chuàng)建當(dāng)前對(duì)象的克隆。當(dāng)直接創(chuàng)建對(duì)象的代價(jià)比較大時(shí),則采用這種模式。

原型模式的工作原理很簡(jiǎn)單:將一個(gè)原型對(duì)象傳給那個(gè)要發(fā)動(dòng)創(chuàng)建的對(duì)象,這個(gè)要發(fā)動(dòng)創(chuàng)建的對(duì)象通過(guò)請(qǐng)求原型對(duì)象拷貝自己來(lái)實(shí)現(xiàn)創(chuàng)建過(guò)程。由于在軟件系統(tǒng)中我們經(jīng)常會(huì)遇到需要?jiǎng)?chuàng)建多個(gè)相同或者相似對(duì)象的情況,因此原型模式在真實(shí)開(kāi)發(fā)中的使用頻率還是非常高的。

一、基本使用

固定的用法就是 實(shí)現(xiàn) Cloneable 接口 ,重寫(xiě) clone()方法。

public class UserProfile implements Cloneable {
    public String userId;
    public String name;
    public String age;
    public UserProfile() {
    }
    public UserProfile(String userId, String name, String age) {
        this.userId = userId;
        this.name = name;
        this.age = age;
    }
    @NonNull
    @Override
    public UserProfile clone() throws CloneNotSupportedException {
        return (UserProfile) super.clone();
    }
}

使用:

            val userProfile = UserProfile("1", "張三", "30")
            YYLogUtils.w("userProfile:$userProfile  name:" + userProfile.name + " age:" + userProfile.age + " skill:" + userProfile.skills)
            try {
                val newUser = userProfile.clone()
                newUser.name = "李四"
                YYLogUtils.w("userProfile:$newUser  name:" + newUser.name + " age:" + newUser.age + " skill:" + newUser.skills)
            } catch (e: Exception) {
                e.printStackTrace()
            }

二、對(duì)象與集合的使用

對(duì)集合與對(duì)象的的處理需要額外的注意。

public class UserProfile implements Cloneable {
    public String userId;
    public String name;
    public String age;
    public List<String> skills;
    public UserProfile() {
    }
    public UserProfile(String userId, String name, String age) {
        this.userId = userId;
        this.name = name;
        this.age = age;
    }
    @NonNull
    @Override
    public UserProfile clone() throws CloneNotSupportedException {
        UserProfile profile = (UserProfile) super.clone();
        profile.name = this.name;
        profile.age = this.age;
        profile.userId = this.userId;
        profile.skills = (List<String>) this.skills.clone();
        return profile;
    }
}

例如我們自己處理數(shù)據(jù)的賦值,那么就會(huì)出現(xiàn)這樣的問(wèn)題,List是無(wú)法clone的。

如果強(qiáng)制這么使用

           val userProfile = UserProfile("1", "張三", "30")
            val skills = listOf("籃球", "游泳", "長(zhǎng)跑", "Java")
            userProfile.skills = skills
            YYLogUtils.w("userProfile:$userProfile  name:" + userProfile.name + " age:" + userProfile.age + " skill:" + userProfile.skills)
            try {
                val newUser = userProfile.clone()
                newUser.name = "李四"
                newUser.skills.add("H5")
                YYLogUtils.w("userProfile:$newUser  name:" + newUser.name + " age:" + newUser.age + " skill:" + newUser.skills)
            } catch (e: Exception) {
                e.printStackTrace()
            }

就會(huì)報(bào)錯(cuò):

我們需要改為ArrayList,因?yàn)橹挥兴棚@示了Cloneable,也就是只有內(nèi)部成員屬性都實(shí)現(xiàn) Cloneable 接口才可以使用

那我們修改類的成員定義為ArrayList

public class UserProfile implements Cloneable {
    public String userId;
    public String name;
    public String age;
    public ArrayList<String> skills = new ArrayList<>();
    public UserProfile() {
    }
    public UserProfile(String userId, String name, String age) {
        this.userId = userId;
        this.name = name;
        this.age = age;
    }
    @NonNull
    @Override
    public UserProfile clone() throws CloneNotSupportedException {
     //兩種方法都可以
//        return (UserProfile) super.clone();
        UserProfile profile = (UserProfile) super.clone();
        profile.name = this.name;
        profile.age = this.age;
        profile.userId = this.userId;
        profile.skills = (ArrayList<String>) this.skills.clone();
        return profile;
    }
}

我們就能打印正確的拷貝值:

集合是可以了,那么我們能不能添加自定義的對(duì)象呢?又會(huì)怎么樣?

我們?cè)賃serProfile類中添加一個(gè)Address的對(duì)象

public class UserProfile implements Cloneable {
    public String userId;
    public String name;
    public String age;
    public UserAddress address;
    public ArrayList<String> skills = new ArrayList<>();
    public UserProfile() {
    }
    public UserProfile(String userId, String name, String age) {
        this.userId = userId;
        this.name = name;
        this.age = age;
    }
    @NonNull
    @Override
    public UserProfile clone() throws CloneNotSupportedException {
       return (UserProfile) super.clone();
    }
}

測(cè)試一下賦值與拷貝

        fun prototypeTest() {
            val userProfile = UserProfile("1", "張三", "30")
            val skills = arrayListOf("籃球", "游泳", "長(zhǎng)跑", "Java")
            userProfile.address = UserAddress("武漢", "楚河漢街")
            userProfile.skills = skills
            YYLogUtils.w(
                "userProfile:$userProfile  name:" + userProfile.name + " age:" + userProfile.age +
                        " skill:" + userProfile.skills + "address:" + userProfile.address + " address-city:" + userProfile.address.city
            )
            try {
                val newUser = userProfile.clone()
                newUser.name = "李四"
                newUser.skills.add("H5")
                newUser.address.city = "長(zhǎng)沙"
                YYLogUtils.w(
                    "userProfile:$newUser  name:" + newUser.name + " age:" + newUser.age +
                            " skill:" + newUser.skills + "address:" + newUser.address + " address-city:" + newUser.address.city
                )
                YYLogUtils.w(
                    "userProfile:$userProfile  name:" + userProfile.name + " age:" + userProfile.age +
                            " skill:" + userProfile.skills + "address:" + userProfile.address + " address-city:" + userProfile.address.city
                )
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }

打印的結(jié)果如下:

可以看到 userProfile 與 newUser 中的 Address 是同一個(gè)對(duì)象,如果一個(gè)對(duì)象修改了 Address 的值,那么另一個(gè)對(duì)象的值就改了。

要修改起來(lái)其實(shí)很簡(jiǎn)單,我們把 Address 對(duì)象重寫(xiě) Cloneable 并實(shí)現(xiàn) clone 方法。

public class UserAddress implements Cloneable {
    public String city;
    public String address;
    public UserAddress() {
    }
    public UserAddress(String city, String address) {
        this.city = city;
        this.address = address;
    }
    @NonNull
    @Override
    public UserAddress clone() throws CloneNotSupportedException {
        return (UserAddress) super.clone();
    }
}

然后在UserProfile的clone方法中手動(dòng)的對(duì)對(duì)象賦值

public class UserProfile implements Cloneable {
    public String userId;
    public String name;
    public String age;
    public UserAddress address;
    public ArrayList<String> skills = new ArrayList<>();
    public UserProfile() {
    }
    public UserProfile(String userId, String name, String age) {
        this.userId = userId;
        this.name = name;
        this.age = age;
    }
    @NonNull
    @Override
    public UserProfile clone() throws CloneNotSupportedException {
        UserProfile profile = (UserProfile) super.clone();
        // 把地址也做一次克隆,達(dá)到深拷貝
        profile.address = address.clone();
        return profile;
    }
}

我們?cè)俅芜\(yùn)行就可以得到我們想要的結(jié)果了:

三、淺拷貝與深拷貝

淺拷貝只會(huì)復(fù)制值,深拷貝不僅會(huì)復(fù)制值也會(huì)復(fù)制引用

淺拷貝又叫影子拷貝,上面我們?cè)诳截愇臋n時(shí)并沒(méi)有把原文檔中的字段都重新構(gòu)造了一遍,而只是拷貝了引用,也就是Address字段引用了之前的Address字段,這樣的話修改新的Address中的內(nèi)容就會(huì)連原Address也改掉了,這就是淺拷貝。

深拷貝就是在淺拷貝的基礎(chǔ)上,對(duì)于引用類型的字段也要采用拷貝的形式,比如上面的Address,而像String、int這些基本數(shù)據(jù)類型則沒(méi)關(guān)系

平常的開(kāi)發(fā)中更推薦大家使用深拷貝,就是對(duì)象實(shí)現(xiàn) Cloneable 并重寫(xiě) clone 方法。

例如這樣深拷貝:

    @NonNull
    @Override
    public UserProfile clone() throws CloneNotSupportedException {
        UserProfile profile = (UserProfile) super.clone();
        // 把地址也做一次克隆,達(dá)到深拷貝
        profile.address = address.clone();
        return profile;
    }

當(dāng)然還有另一種用法,既不是淺拷貝,也不是深拷貝,例如Intent的clone方法

@Override
public Object clone() {
    return new Intent(this);
}
private Intent(Intent o, boolean all) {
    this.mAction = o.mAction;
    this.mData = o.mData;
    this.mType = o.mType;
    this.mPackage = o.mPackage;
    this.mComponent = o.mComponent;
    if (o.mCategories != null) {
        this.mCategories = new ArraySet<String>(o.mCategories);
    }
}

Intent 的 clone 方法實(shí)際上是通過(guò)new對(duì)象的方法來(lái)實(shí)現(xiàn)的,并沒(méi)有調(diào)用super.clone()。

四、Kotlin的應(yīng)用

那么在Kotlin中的使用又有什么不同呢?

其實(shí)在Kotlin中調(diào)用普通的類是和Java一樣的,但是如果是data class 的數(shù)據(jù)類,內(nèi)部有copy方法可以快速實(shí)現(xiàn)clone的對(duì)象。

那么它是淺拷貝還是深拷貝呢?

data class Company(
    var name: String,
    var year: String,
    var address: UserAddress,
)

使用:

    val company = Company("百度", "2001年", UserAddress("北京", "海淀區(qū)"))
    YYLogUtils.w("company:$company")
    val newCompany = company.copy()
    newCompany.name = "網(wǎng)易"
    newCompany.address.city = "杭州"
    YYLogUtils.w("newCompany:$newCompany")
    YYLogUtils.w("company:$company")

我們?cè)?data class 中定義一個(gè)普通的對(duì)象 Address ,我們打印的值如下:

可以看到打印的結(jié)果,默認(rèn)的 data class copy 為淺拷貝。

那么如何實(shí)現(xiàn)深拷貝呢?我們仿造Java寫(xiě) Cloneable 和 clone() 試試。

data class Company(
    var name: String,
    var year: String,
    var address: UserAddress,
) : Cloneable {
    public override fun clone(): Company {
        val company: Company = super.clone() as Company
        company.address = address.clone()
        return company
    }
}

使用的時(shí)候我們不使用copy()而使用clone()

    val company = Company("百度", "2001年", UserAddress("北京", "海淀區(qū)"))
    YYLogUtils.w("company:$company addressCity:${company.address.city}")
    val newCompany = company.clone()
    newCompany.name = "網(wǎng)易"
    newCompany.address.city = "杭州"
    YYLogUtils.w("newCompany:$newCompany  addressCity:${newCompany.address.city}")
    YYLogUtils.w("company:$company  addressCity:${company.address.city}")

那么打印的結(jié)果確實(shí)成了深拷貝,就是我們想要的。

另外一些其他的方法,例如一些第三庫(kù) github.com/bennyhuo/Ko… 給對(duì)象 data class 加注解,讓其實(shí)現(xiàn)深拷貝的邏輯。

還有一些方法是通過(guò)Kotlin反射庫(kù)的一些方式實(shí)現(xiàn),擴(kuò)展方法如下

//data class 對(duì)象的深拷貝
fun <T : Any> T.deepCopy(): T {
    //如果不是數(shù)據(jù)類,直接返回
    if (!this::class.isData) {
        return this
    }
    //拿到構(gòu)造函數(shù)
    return this::class.primaryConstructor!!.let { primaryConstructor ->
        primaryConstructor.parameters.map { parameter ->
            //轉(zhuǎn)換類型
            //最終value=第一個(gè)參數(shù)類型的對(duì)象
            val value = (this::class as KClass<T>).memberProperties.first {
                it.name == parameter.name
            }.get(this)
            //如果當(dāng)前類(這里的當(dāng)前類指的是參數(shù)對(duì)應(yīng)的類型,比如說(shuō)這里如果非基本類型時(shí))是數(shù)據(jù)類
            if ((parameter.type.classifier as? KClass<*>)?.isData == true) {
                parameter to value?.deepCopy()
            } else {
                parameter to value
            }
            //最終返回一個(gè)新的映射map,即返回一個(gè)屬性值重新組合的map,并調(diào)用callBy返回指定的對(duì)象
        }.toMap().let(primaryConstructor::callBy)
    }
}

如果報(bào)錯(cuò)的話,可以看看是不是Kotlin反射庫(kù)沒(méi)有導(dǎo)入,比如我項(xiàng)目的Kotlin版本為1.5.31 , 那么我導(dǎo)入了一個(gè)反射庫(kù)依賴如下:

api 'org.jetbrains.kotlin:kotlin-reflect:1.5.31'

使用起來(lái)很簡(jiǎn)單,直接使用擴(kuò)展方法即可:

    val company = Company("百度", "2001年", Address("北京", "海淀區(qū)"))
    YYLogUtils.w("company:$company addressCity:${company.address.city}")
    val newCompany = company.deepCopy()
    newCompany.name = "網(wǎng)易"
    newCompany.address.city = "杭州"
    YYLogUtils.w("newCompany:$newCompany  addressCity:${newCompany.address.city}")
    YYLogUtils.w("company:$company  addressCity:${company.address.city}")

打印的結(jié)果符合我們的預(yù)期:

這種方法需要注意的是只支持 data class ,內(nèi)部的對(duì)象也需要時(shí)data class 如果是普通類或Java類的對(duì)象都是不行的。并且大家需要注意的是這么,深拷貝的性能是比淺拷貝要差一點(diǎn)的。特別是使用這樣擴(kuò)展方式的形式,他的性能比重寫(xiě) Cloneable 和 clone()這樣的方式性能要差。如果沒(méi)有必要還是推薦使用淺拷貝實(shí)現(xiàn)。

比如之前的文章 MVI框架的展示。我們就是使用淺拷貝,我們就是要改變內(nèi)存中List的值

    private val _viewStates: MutableLiveData<Demo14ViewState> = MutableLiveData(Demo14ViewState())
    //只需要暴露一個(gè)LiveData,包括頁(yè)面所有狀態(tài)
    val viewStates: LiveData<Demo14ViewState> = _viewStates
    //當(dāng)前頁(yè)面所需的數(shù)據(jù)與狀態(tài)
    data class Demo14ViewState(
        val industrys: List<Industry> = emptyList(),
        val schools: List<SchoolBean> = emptyList(),
        var isChanged: Boolean = false
    ) : BaseViewState()
    //獲取學(xué)校數(shù)據(jù)
    private fun requestSchool() {
        viewModelScope.launch {
            //開(kāi)始Loading
            loadStartLoading()
            val result = mRepository.getSchool()
            result.checkSuccess {
                _viewStates.setState {
                    copy(schools = it ?: emptyList())
                }
            }
            loadHideProgress()
        }
    }

例如上文中的copy用法,淺拷貝,當(dāng)我們調(diào)用接口獲取到學(xué)校的是時(shí)候就賦值學(xué)校數(shù)據(jù),淺拷貝一個(gè)對(duì)象,并對(duì)它賦值,再把它設(shè)置給LiveData,當(dāng)我們獲取到行業(yè)數(shù)據(jù)的時(shí)候,一樣的操作,又并不會(huì)對(duì)學(xué)校的數(shù)據(jù)做修改,又修改了原生LiveData中的Value值,因?yàn)閮蓚€(gè)對(duì)象的引用是不同的,這樣就可以觸發(fā)LiveData的通知。也是一個(gè)比較典型的應(yīng)用。

如果是深拷貝,當(dāng)然也能實(shí)現(xiàn)同樣的邏輯,就是會(huì)麻煩一點(diǎn),性能也沒(méi)有淺拷貝好,所以這個(gè)場(chǎng)景使用的是簡(jiǎn)單的淺拷貝。

總結(jié)

原型模式本質(zhì)上就是對(duì)象的拷貝,容易出現(xiàn)的問(wèn)題也都是深拷貝、淺拷貝。使用原型模式可以解決構(gòu)建復(fù)雜對(duì)象的資源消耗問(wèn)題,能夠在某些場(chǎng)景下提升創(chuàng)建對(duì)象的效率。

優(yōu)點(diǎn):

  • 原型模式是在內(nèi)存中二進(jìn)制流的拷貝,要比直接new一個(gè)對(duì)象性能好很多,特別是要在一個(gè)循環(huán)體內(nèi)產(chǎn)生大量對(duì)象時(shí),原型模式可能更好的體現(xiàn)其優(yōu)點(diǎn)。
  • 還有一個(gè)重要的用途就是保護(hù)性拷貝,也就是對(duì)某個(gè)對(duì)象對(duì)外可能是只讀的,為了防止外部對(duì)這個(gè)只讀對(duì)象的修改,通??梢酝ㄟ^(guò)返回一個(gè)對(duì)象拷貝的形式實(shí)現(xiàn)只讀的限制。

缺點(diǎn):

  • 這既是它的優(yōu)點(diǎn)也是缺點(diǎn),直接在內(nèi)存中拷貝,構(gòu)造函數(shù)是不會(huì)執(zhí)行的,在實(shí)際開(kāi)發(fā)中應(yīng)該注意這個(gè)潛在問(wèn)題。優(yōu)點(diǎn)是減少了約束,缺點(diǎn)也是減少了約束,需要大家在實(shí)際應(yīng)用時(shí)考慮。
  • 通過(guò)實(shí)現(xiàn)Cloneable接口的原型模式在調(diào)用clone函數(shù)構(gòu)造實(shí)例時(shí)并不一定比通過(guò)new操作速度快,只有當(dāng)通過(guò)new構(gòu)造對(duì)象較為耗時(shí)或者說(shuō)成本較高時(shí),通過(guò)clone方法才能夠獲得效率上的提升。

平常的開(kāi)發(fā)中,淺拷貝和深拷貝都有各自的應(yīng)用場(chǎng)景,如果 class 中都是基礎(chǔ)數(shù)據(jù),那也不需要關(guān)心是淺拷貝還是深拷貝,直接淺拷貝即可,如果包含對(duì)象,那么就要看自己的需求來(lái)選擇是淺拷貝還是深拷貝了。

以上就是Android常用設(shè)計(jì)模式之原型模式詳解的詳細(xì)內(nèi)容,更多關(guān)于Android 設(shè)計(jì)模式原型模式的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Android 快速使用正則表達(dá)式,校驗(yàn)身份證號(hào)的實(shí)例

    Android 快速使用正則表達(dá)式,校驗(yàn)身份證號(hào)的實(shí)例

    下面小編就為大家分享一篇Android 快速使用正則表達(dá)式,校驗(yàn)身份證號(hào)的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-01-01
  • Android實(shí)現(xiàn)自定義ImageView的圓角矩形圖片效果

    Android實(shí)現(xiàn)自定義ImageView的圓角矩形圖片效果

    android顯示圓角矩形的圖片其原理就是首先獲取到圖片的Bitmap,然后進(jìn)行裁剪對(duì)應(yīng)的圓角矩形的bitmap,然后在onDraw()進(jìn)行繪制圓角矩形圖片輸出
    2018-05-05
  • Android?WebView基礎(chǔ)舉例詳解

    Android?WebView基礎(chǔ)舉例詳解

    在Android開(kāi)發(fā)中Webview是一個(gè)重要的組件,它允許我們?cè)谠鷳?yīng)用中內(nèi)嵌網(wǎng)頁(yè)內(nèi)容,提供混合式應(yīng)用的用戶體驗(yàn),這篇文章主要給大家介紹了關(guān)于Android?WebView基礎(chǔ)的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-07-07
  • Android自定義控件實(shí)現(xiàn)底部菜單(上)

    Android自定義控件實(shí)現(xiàn)底部菜單(上)

    這篇文章主要為大家詳細(xì)介紹了Android自定義控件實(shí)現(xiàn)底部菜單的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-01-01
  • 最新評(píng)論