java 可变参数方法_Java方法中的参数太多,第7部分:可变状态
java 可變參數方法
在我的系列文章的第七篇中,有關解決Java方法或構造函數中過多參數的問題 ,我著眼于使用狀態來減少傳遞參數的需要。 我等到本系列的第七篇文章來解決這個問題的原因之一是,它是我最不喜歡的減少傳遞給方法和構造函數的參數的方法之一。 也就是說,這種方法有多種風味,我絕對更喜歡某些風味。
在所有軟件開發中,使用狀態減少參數的方法中最著名,最被嘲笑的方法可能是使用全局變量 。 盡管從語義上講Java沒有全局變量可能是準確的,但事實是,無論是好是壞,都可以通過公共靜態構造在Java中實現全局變量的等效項 。 在Java中實現此目標的一種特別流行的方法是通過有狀態的Singleton 。
馬丁·福勒(Martin Fowler)在《企業應用程序架構模式》中寫道:“任何全球數據在被證明無辜之前總是有罪的。” 出于多種原因 ,Java中的全局變量和“類全局”構造被認為是不好的形式。 它們會使開發人員難以維護和讀取代碼來知道值的定義位置,最后更改甚至是來源。 就其本質和意圖而言,全局數據違反了封裝和數據隱藏的原理。
Mi?koHevery 用面向對象的語言編寫了有關靜態全局變量問題的以下文章:
靜態訪問全局狀態不會向使用全局狀態的構造函數和方法的讀者闡明那些共享的依賴關系。 Global State和Singletons使API依賴其真正的依賴關系。 …全局狀態的根本問題是它可以全局訪問。 在理想情況下,一個對象應該只能與直接傳遞給其他對象(通過構造函數或方法調用)的其他對象進行交互。
具有全局可用狀態減少了對參數的需求,因為如果兩個對象已經可以直接訪問該數據,則無需將其傳遞給另一個對象。 但是,正如Hevery所說,這完全與面向對象設計的意圖正交。
隨著并發應用變得越來越普遍,可變狀態也是一個日益嚴重的問題。 在他關于Scala的JavaOne 2012演示中 , Scala的創建者Martin Odersky指出,在高度并發的世界中“擁有的每一個可變狀態都是一種責任”,并補充說問題是“由并發線程訪問共享可變狀態導致的不確定性。 ”
盡管有避免避免可變狀態的原因,但是它仍然是軟件開發中普遍使用的方法。 我認為這樣做的原因有很多,其中包括編寫可變狀態共享代碼非常容易,可變共享代碼的確提供了訪問的便利。 某些類型的可變數據很受歡迎,因為這些類型的可變數據已經被教授和學習多年了。 最后,可變狀態可能是最合適的解決方案,這是三遍。 出于最后一個原因并且為了完整起見,我現在來看一下可變狀態的使用如何減少方法必須期望的參數數量。
有狀態的單例和靜態變量
一個Java實現的辛格爾頓和其他公共Java 靜態字段通常可用于同一范圍內的任何Java代碼的Java虛擬機 (JVM)和加載與同一 類加載器 [對于更多細節 ,請參見當是一個Singleton不是單身? ]。
通用存儲的任何數據(至少從JVM / classloader角度而言)已可供同一JVM中的客戶端代碼使用并由相同的類加載器加載。 因此,無需在同一JVM / classloader組合中的客戶端與方法或構造函數之間傳遞數據。
實例狀態
雖然“靜態”被認為是“全局可用的”,但也可以類似的方式使用較窄的實例級狀態,以減少在同一類的方法之間傳遞參數的需求。 與全局變量相比,此方法的一個優點是可訪問性僅限于類的實例( 私有字段)或類的子實例( 私有字段)。 當然,如果字段是public ,則可訪問性是相當開放的,但是相同的數據不會自動提供給同一JVM / classloader中的其他代碼。
下一個代碼清單演示了狀態數據如何以及有時用于減少給定類內部的兩個方法之間對參數的需求。
用于避免傳遞參數的實例狀態示例
/*** Simple example of using instance variable so that there is no need to* pass parameters to other methods defined in the same class.*/public void doSomethingGoodWithInstanceVariables(){this.person =Person.createInstanceWithNameAndAddressOnly(new FullName.FullNameBuilder(new Name("Flintstone"), new Name("Fred")).createFullName(),new Address.AddressBuilder(new City("Bedrock"), State.UN).createAddress());printPerson();}/*** Prints instance of Person without requiring it to be passed in because it* is an instance variable.*/public void printPerson(){out.println(this.person);}上面的示例經過精心設計和簡化,但確實說明了這一點:實例變量person可以由同一類中定義的其他實例方法訪問,因此不需要在這些實例方法之間傳遞實例。 這確實減少了潛在的 ( public可訪問性意味著可以由外部方法使用)內部方法的簽名,但是還引入了狀態,現在意味著所調用的方法會影響同一對象的狀態。 換句話說,不必傳遞參數的好處是以另一個可變狀態為代價的。 為了進行比較,權衡的另一面需要傳遞Person實例,因為它不是實例變量,因此需要傳遞該實例。
傳遞參數而不使用實例變量的示例
/*** Simple example of passing a parameter rather than using an instance variable.*/public void doSomethingGoodWithoutInstanceVariables(){final Person person =Person.createInstanceWithNameAndAddressOnly(new FullName.FullNameBuilder(new Name("Flintstone"), new Name("Fred")).createFullName(),new Address.AddressBuilder(new City("Bedrock"), State.UN).createAddress());printPerson(person);}/*** Prints instance of Person that is passed in as a parameter.* * @param person Instance of Person to be printed.*/public void printPerson(final Person person){out.println(person);}前兩個代碼清單說明可以通過使用實例狀態來減少參數傳遞。 我通常更喜歡不要僅使用實例狀態來避免參數傳遞。 如果出于其他原因需要實例狀態,那么減少傳遞的參數是一個不錯的附帶好處,但是我不喜歡引入不必要的實例狀態來簡單地刪除或減少參數數量。 盡管有時候在大型單線程環境中,精簡參數的可讀性可能已經證明了實例狀態的合理性,但我認為,在越來越多的情況下,精簡參數帶來的輕微可讀性不值得以非線程安全的類為代價多線程的世界。 我仍然不喜歡在同一個類的方法之間傳遞大量參數,但是我可以使用parameters對象 (也許使用package-private scope class )來減少這些參數的數量并將該參數對象傳遞給周圍而不是大量的參數。
JavaBean樣式構造
JavaBeans約定/樣式已在Java開發社區中變得非常流行。 諸如Spring Framework和Hibernate之類的許多框架都依賴于遵循JavaBeans約定的類,并且某些標準(如Java Persistence API)也圍繞JavaBeans約定構建。 JavaBeans風格流行的原因有很多,包括它的易用性以及針對遵循此約定的此代碼使用反射的能力,以避免進行額外的配置。
JavaBean樣式的基本思想是使用無參數構造函數實例化一個對象,然后通過單參數“ set”方法設置其字段,然后通過無參數“ get”方法訪問其字段。 在下面的代碼清單中將對此進行演示。 第一個清單顯示了一個簡單的PersonBean類示例,該類具有無參數構造函數以及getter和setter方法。 該代碼清單還包括它使用的某些JavaBeans樣式的類。 該代碼清單后跟使用該JavaBean樣式類的代碼。
JavaBeans樣式類的示例
public class PersonBean {private FullNameBean name;private AddressBean address;private Gender gender;private EmploymentStatus employment;private HomeownerStatus homeOwnerStatus;/** No-arguments constructor. */public PersonBean() {}public FullNameBean getName(){return this.name;}public void setName(final FullNameBean newName){this.name = newName;}public AddressBean getAddress(){return this.address;}public void setAddress(final AddressBean newAddress){this.address = newAddress;}public Gender getGender(){return this.gender;}public void setGender(final Gender newGender){this.gender = newGender;}public EmploymentStatus getEmployment(){return this.employment;}public void setEmployment(final EmploymentStatus newEmployment){this.employment = newEmployment;}public HomeownerStatus getHomeOwnerStatus(){return this.homeOwnerStatus;}public void setHomeOwnerStatus(final HomeownerStatus newHomeOwnerStatus){this.homeOwnerStatus = newHomeOwnerStatus;} }/*** Full name of a person in JavaBean style.* * @author Dustin*/ public final class FullNameBean {private Name lastName;private Name firstName;private Name middleName;private Salutation salutation;private Suffix suffix;/** No-args constructor for JavaBean style instantiation. */private FullNameBean() {}public Name getFirstName(){return this.firstName;}public void setFirstName(final Name newFirstName){this.firstName = newFirstName;}public Name getLastName(){return this.lastName;}public void setLastName(final Name newLastName){this.lastName = newLastName;}public Name getMiddleName(){return this.middleName;}public void setMiddleName(final Name newMiddleName){this.middleName = newMiddleName;}public Salutation getSalutation(){return this.salutation;}public void setSalutation(final Salutation newSalutation){this.salutation = newSalutation;}public Suffix getSuffix(){return this.suffix;}public void setSuffix(final Suffix newSuffix){this.suffix = newSuffix;}@Overridepublic String toString(){return this.salutation + " " + this.firstName + " " + this.middleName+ this.lastName + ", " + this.suffix;} }package dustin.examples;/*** Representation of a United States address (JavaBeans style).* * @author Dustin*/ public final class AddressBean {private StreetAddress streetAddress;private City city;private State state;/** No-arguments constructor for JavaBeans-style instantiation. */private AddressBean() {}public StreetAddress getStreetAddress(){return this.streetAddress;}public void setStreetAddress(final StreetAddress newStreetAddress){this.streetAddress = newStreetAddress;}public City getCity(){return this.city;}public void setCity(final City newCity){this.city = newCity;}public State getState(){return this.state;}public void setState(final State newState){this.state = newState;}@Overridepublic String toString(){return this.streetAddress + ", " + this.city + ", " + this.state;} }JavaBeans樣式實例化和填充的示例
public PersonBean createPerson(){final PersonBean person = new PersonBean();final FullNameBean personName = new FullNameBean();personName.setFirstName(new Name("Fred"));personName.setLastName(new Name("Flintstone"));person.setName(personName);final AddressBean address = new AddressBean();address.setStreetAddress(new StreetAddress("345 Cave Stone Road"));address.setCity(new City("Bedrock"));person.setAddress(address);return person;}剛剛顯示的示例演示了如何使用JavaBeans樣式方法。 這種方法做出了一些讓步,以減少將大量參數傳遞給類的構造函數的需要。 相反,不會將任何參數傳遞給構造函數,并且必須設置每個需要的屬性。 JavaBeans樣式方法的優點之一是,與具有大量參數的構造函數相比,可讀性得到了增強,因為希望每個“ set”方法都以可讀的方式命名。
JavaBeans方法易于理解,在構造函數的情況下,絕對可以達到減少冗長參數的目的。 但是,這種方法也有一些缺點。 一個優點是很多繁瑣的客戶端代碼,它們一次實例化對象并設置其屬性。 忽略設置必需屬性的方法很容易,因為在不離開JavaBeans約定的情況下,編譯器無法強制設置所有必需參數。 也許最具破壞性,在最后一個代碼清單中實例化了幾個對象,這些對象從實例化到調用最終“設置”方法的時間,處于不同的不完整狀態。 在這段時間內,對象處于真正的“未定義”或“不完整”狀態。 “設置”方法的存在必然意味著該類的屬性不能為final ,從而使整個對象高度可變。
關于Java中JavaBeans模式的普遍使用 ,一些可靠的作者對它的價值提出了質疑 。 艾倫·霍魯布(Allen Holub)的有爭議的文章為什么getter和setter方法是邪惡的,開始時沒有任何保留:
盡管getter / setter方法在Java中很常見,但它們并不是面向對象(OO)的。 實際上,它們會破壞代碼的可維護性。 而且,大量的getter和setter方法的存在是一個危險信號,即從OO角度來看,該程序不一定設計得很好。
喬什·布洛赫(Josh Bloch)用較不那么有力和更柔和的說服力說著JavaBeans的getter / setter風格:“ JavaBeans模式有其自身的嚴重缺點”( Effective Java ,第二版, 項目#2 )。 在這種情況下,Bloch推薦使用構建器模式代替對象構建。
當我出于其他原因選擇的框架需要JavaBeans get / set樣式并且使用該框架的理由證明了我的理由時,我不反對使用JavaBeans get / set樣式。 在某些方面,JavaBeans樣式類也特別適合,例如與數據存儲進行交互并保存數據存儲中的數據以供應用程序使用。 但是,我不喜歡使用JavaBeans樣式實例化問題,只是為了避免傳遞參數。 為此,我更喜歡其他方法之一,例如builder。
優勢與優勢
我在本文中介紹了減少方法或構造函數的參數數量的不同方法,但是它們也有相同的權衡:暴露可變狀態以減少或消除必須傳遞給方法或構造函數的參數數量。到構造函數。 這些方法的優點是簡單,通常可讀(盡管“全局變量”可能很難讀取)以及易于首次編寫和使用。 當然,從本文的角度來看,它們的最大優點是它們通常消除了傳遞任何參數的需要。
成本與劣勢
該職位分享的所有方法的特點是易變狀態的暴露。 如果在高度并發的環境中使用代碼,則可能導致極高的成本。 當對象狀態暴露給任何人隨意修改時,存在一定程度的不可預測性。 可能很難知道哪個代碼進行了錯誤的更改或未能進行必要的更改(例如,在填充新實例化的對象時未能調用“ set”方法)。
結論
甚至我之前介紹過的某些減少參數的方法(例如自定義類型和參數對象)都可以以(可選)可變狀態的方式實現,但這些方法不需要可變狀態。 相反,本文中涉及的減少方法和構造函數參數的方法確實需要可變狀態。
盡管有一些缺點,但本文中介紹的某些方法還是非常受歡迎的。 這可能是出于多種原因,包括在流行框架中的普遍使用(迫使框架的用戶使用該樣式,還為其他人提供了自己的代碼開發示例)。 這些方法之所以受歡迎的其他原因是,相對容易的初始開發以及使用這些方法進行設計時似乎(欺騙性)相對較少的思考。 總的來說,我更愿意花更多的設計和實現工作來使用構建器,而在可行時使用較少可變的方法。 但是,在某些情況下,這些可變狀態方法可以很好地減少傳遞的參數數量,并且不會帶來比已經存在的更多的風險。 我的感覺是,Java開發人員應該仔細考慮使用任何可變的Java類,并確保可變性是所希望的,或者是使用可變狀態方法的原因所證明的成本。
翻譯自: https://www.javacodegeeks.com/2013/11/too-many-parameters-in-java-methods-part-7-mutable-state.html
java 可變參數方法
總結
以上是生活随笔為你收集整理的java 可变参数方法_Java方法中的参数太多,第7部分:可变状态的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux进程守护工具(linux 进程
- 下一篇: 自动化用户特定实体的访问控制