最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Android常用設計模式之原型模式詳解

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

前言

什么是原型模式?

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

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

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

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

一、基本使用

固定的用法就是 實現(xiàn) Cloneable 接口 ,重寫 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()
            }

二、對象與集合的使用

對集合與對象的的處理需要額外的注意。

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ù)的賦值,那么就會出現(xiàn)這樣的問題,List是無法clone的。

如果強制這么使用

           val userProfile = UserProfile("1", "張三", "30")
            val skills = listOf("籃球", "游泳", "長跑", "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()
            }

就會報錯:

我們需要改為ArrayList,因為只有它才顯示了Cloneable,也就是只有內(nèi)部成員屬性都實現(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;
    }
}

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

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

我們再UserProfile類中添加一個Address的對象

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();
    }
}

測試一下賦值與拷貝

        fun prototypeTest() {
            val userProfile = UserProfile("1", "張三", "30")
            val skills = arrayListOf("籃球", "游泳", "長跑", "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 = "長沙"
                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 是同一個對象,如果一個對象修改了 Address 的值,那么另一個對象的值就改了。

要修改起來其實很簡單,我們把 Address 對象重寫 Cloneable 并實現(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方法中手動的對對象賦值

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();
        // 把地址也做一次克隆,達到深拷貝
        profile.address = address.clone();
        return profile;
    }
}

我們再次運行就可以得到我們想要的結(jié)果了:

三、淺拷貝與深拷貝

淺拷貝只會復制值,深拷貝不僅會復制值也會復制引用

淺拷貝又叫影子拷貝,上面我們在拷貝文檔時并沒有把原文檔中的字段都重新構造了一遍,而只是拷貝了引用,也就是Address字段引用了之前的Address字段,這樣的話修改新的Address中的內(nèi)容就會連原Address也改掉了,這就是淺拷貝。

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

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

例如這樣深拷貝:

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

當然還有另一種用法,既不是淺拷貝,也不是深拷貝,例如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 方法實際上是通過new對象的方法來實現(xiàn)的,并沒有調(diào)用super.clone()。

四、Kotlin的應用

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

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

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

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")

我們再 data class 中定義一個普通的對象 Address ,我們打印的值如下:

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

那么如何實現(xiàn)深拷貝呢?我們仿造Java寫 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
    }
}

使用的時候我們不使用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é)果確實成了深拷貝,就是我們想要的。

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

還有一些方法是通過Kotlin反射庫的一些方式實現(xiàn),擴展方法如下

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

如果報錯的話,可以看看是不是Kotlin反射庫沒有導入,比如我項目的Kotlin版本為1.5.31 , 那么我導入了一個反射庫依賴如下:

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

使用起來很簡單,直接使用擴展方法即可:

    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é)果符合我們的預期:

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

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

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

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

如果是深拷貝,當然也能實現(xiàn)同樣的邏輯,就是會麻煩一點,性能也沒有淺拷貝好,所以這個場景使用的是簡單的淺拷貝。

總結(jié)

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

優(yōu)點:

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

缺點:

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

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

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

相關文章

最新評論

中牟县| 南皮县| 修武县| 云安县| 恩施市| 玉环县| 永吉县| 溧水县| 玛沁县| 仙居县| 得荣县| 海兴县| 班戈县| 西和县| 兰州市| 天水市| 惠州市| 武冈市| 封丘县| 西城区| 阿坝| 滨州市| 诏安县| 神农架林区| 平武县| 辽中县| 丰县| 鄄城县| 卓尼县| 博乐市| 沅江市| 石渠县| 贞丰县| 德阳市| 鄂伦春自治旗| 本溪市| 明水县| 翁牛特旗| 醴陵市| 天长市| 霍山县|