一、常量接口模式
在一個軟件系統中會使用一些常量,一種流行的做法就是把相關的常量放在一個專門的常量接口中定義,例如:
public interface MyConstants { public static final double MATH_PI = 3.1415926; public static final double MATH_E = 2.71828; }
以下Circle類需要訪問以上MATH_PI常量,一種方式是采用直接訪問方式,如下:
public class Circle { private double r;//半徑 public Circle(double r){ this.r = r; } public double getCircumference(){ return 2 * r * MyConstants.MATH_PI; } }
在JDK1.5中引入了”import static“語句,它允許類A直接訪問另一個接口B或類B中的靜態常量,而不必指定接口B或類B的名字,而且類A無須實現接口B或者繼承類B。如下:
import static com.FinalInterface.MyConstants.*; public class Circle { private double r;//半徑 public Circle(double r){ this.r = r; } public double getCircumference(){ return 2 * r * MATH_PI; } }
import static 語句既可以簡化編程,又能防止Circle類繼承并公開MyConstants中的靜態常量。
二、標識類模式
動物飼養員給貓喂魚,給狗喂骨頭,給老虎喂雞,給熊貓喂竹子,給馬喂草,,這里的魚、骨頭、雞、竹子、草都可以作為動物的食物,因此從中抽象出Food接口。
public interface Food() //Food接口中沒有任何內容
Food接口不包含任何方法,它僅僅表示一種抽象類型,所有實現該接口的類意味著可以作為食物,例如魚類Fish實現了Food接口,因此它可以作為其他動物的食物
public class Fish extends Animal implments Food(...)
動物飼養員Feeder類的feed()方法的定義如下:
public void feed (Animal animal,Food food){...}
feed方法的food參數為Food類型,表示只能把可作為食物的對象喂給動物,以下程序表示試圖給狗喂書,由于Book類沒有實現Food接口,因此導致編譯錯誤
Book book =new Book(); feeder.feed(dog,book); //編譯錯誤
如果把feed方法的food參數改為Object類型:
public void feed(Animal animal,Object food){...}
那么就無法借助java編譯器來對傳給feed方法的food參數進行語義上的約束,例如以下程序表示給狗喂一個字符串對象,java編譯器會認為這是合法的
Object anyObject=new String("hello"); feeder.feed(dog,anyObject); //編譯成功
Food接口被稱為標識類型接口,這種接口沒有任何方法,代表一種抽象類型,在JDK中,有如下兩個典型的標識類型接口
① java.io.Serializable接口:實現該接口的類的實例可以被序列化
②java.io.Remote接口:實現該接口的類的實例可以作為遠程對象
三、適配器模式
適配器是兩個接口的中間過度,比如電腦必須要保證電壓在15V,而電源的電壓是220V,則需要一個適配器將220V轉換成15V,因此適配器就是接口的轉換作用。
interface Source{ public int add(int a,int b); } class SourceImpl implements Source{ public int add(int a,int b){ return a+b; } } interface Target{ public int addOne(int a); } class TargetImpl implements Target{ private Source s ; public TargetImpl(Source s){ this.s = s; } public int addOne(int a){ return s.add(a,1); } } public class AdapterDemo{ public static void main(String args[]){ Source s = new SourceImpl(); Target t = new TargetImpl(s); System.out.println("2+1="+t.addOne(2)); } }
四、定制服務模式
定制服務的模式也可應用到面向對象的軟件開發領域。當一個系統能對外提供多種類型的服務時,一種方式是粗粒度的接口,把所有的服務放在一個接口中聲明,這個接口臃腫龐大,所有的使用者都訪問同一個接口。還有一種方式就是設計精粒度接口,對服務精心分類,把相關的一組服務放在一個接口中,通過對接口的繼承,可以派生出新的接口,針對使用者的需求提供特定的接口
上表中的極速精英套餐SuperSpeedCombo和金融專網套餐FinanceCombo屬于兩種定制的服務接口,它們可以通過繼承以上5個精粒度的接口而形成,這樣的接口也稱為復合接口。
服務接口定制好以后,接下來的問題是如何實現這些接口。為了提高代碼的可重用性,類的粒度也應該盡可能小,所以首先為精粒度的接口提供實現類。
以下列出其中的一個服務實現類:
public class BroadbandServiceImpl implements BroadbandService{ private int speed;//網速 public BroadbandServiceImpl(int speed){ this.speed = speed; } //連接網絡 public void connect(String username,String password){...} //斷開網絡 public void disconnect(){...} }
同上,將精粒度的接口一一創建實現類,得到精粒度的類。
那么對于SuperSpeedCombo 和 FinanceCombo 復合接口,如何實現它們呢?以 SuperSpeedCombo接口的實現類 SuperSpeedComboImpl為例,可以采用組合手段,復用 BroadbandService接口、VirusKillingService接口和MailboxService接口的實現類的程序代碼。
那么什么是組合關系呢?在這再復習一下,所謂的組合和繼承都是提高代碼可重用性的手段,繼承最大的弱點就是破壞封裝,子類和父類之間緊密耦合,子類依賴于父類的實現,子類缺乏獨立性,而組合關系不會破壞封裝,整體類與局部類之間松耦合,彼此相互獨立。當然組合關系也有缺點:創建整體類的對象時需要創建所有局部類的對象,而繼承關系在創建子類的對象時無須創建父類的對象。
比如要在SuperSpeedComboImpl采用組合手段加入寬帶上網服務BroadbandService:
public class SuperSpeedComboImpl implements SuperSpeedCombo{ private BroadbandServiceImpl BroadbandService; public SuperSpeedComboImpl(BroadbandServiceImpl BroadbandService){ this.BroadbandService = BroadbandService; } }
此外,對于極速精英套餐和金融專網套餐,都有付費方式和價格這些屬性,可以把這些屬性放到同一個Payment中,這符合構建精粒度的對象模型的原則,下面是Payment的源程序:
public class Payment{ public static final String TYPE_PER_YEAR="按年付費"; public static final String TYPE_PER_MONTH="按月付費"; private String type;//付費方式 private double price;//價格 public Payment(String type, double price) { this.type = type; this.price = price; } //省略type屬性和price屬性的get/set方法 ... }
SuperSpeedComboImpl類的源程序如下:
public class SuperSpeedComboImpl implements SuperSpeedCombo{ private BroadbandServiceImpl BroadbandService; private VirusKillingService virusKillingService; private MailboxService mailboxService; private Payment payment; public SuperSpeedComboImpl(BroadbandServiceImpl broadbandService, VirusKillingService virusKillingService, MailboxService mailboxService, Payment payment) { super(); BroadbandService = broadbandService; this.virusKillingService = virusKillingService; this.mailboxService = mailboxService; this.payment = payment; } public BroadbandServiceImpl getBroadbandService() { return BroadbandService; } public void setBroadbandService(BroadbandServiceImpl broadbandService) { BroadbandService = broadbandService; } public VirusKillingService getVirusKillingService() { return virusKillingService; } public void setVirusKillingService(VirusKillingService virusKillingService) { this.virusKillingService = virusKillingService; } public MailboxService getMailboxService() { return mailboxService; } public void setMailboxService(MailboxService mailboxService) { this.mailboxService = mailboxService; } public Payment getPayment() { return payment; } public void setPayment(Payment payment) { this.payment = payment; } }
下面創建一個極速精英套餐服務的一個實例:
//創建付費信息,按年付費,價格1555 Payment payment = new Payment(Payment.TYPE_PER_MONTH,1555); //創建寬帶上網服務,網速2Mbps BroadbandService broadbandService = new BroadbandServiceImpl(2); //創建郵箱服務,50MB容量 MailboxService mailboxService = new MialboxServiceImpl(50); //創建在線殺毒服務 VirusKillingService virusKillingService = new VirusKillingServiceImpl(); //創建極速精英套餐服務 SuperSpeedCombo superSpeedCombo = new SuperSpeedComboImpl(broadbandService,mailboxService,virusKillingService,payment);
文章列表