ISP (接口隔離原則)主要強調的是,interface必須是內聚的,也就是說不應該出現 "胖" interface,
如果出現了 "胖" interface 代表他應該是可以分解成多組。
底下是一個 "胖" interface 的範例
首先一開始客戶要求我們做一台車,很簡單的我們直接使用了 interface Vehicle ,
之後不管是摩托車,跑車,房車,中古車 都可以使用他
interface Vehicle
{
void Run();
}
class Porsche911 implements Vehicle
{
@Override
public void Run() {
// TODO Auto-generated method stub
System.out.println("Porsche911 Running");
}
}
public class Car implements Vehicle{
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Vehicle carVehicle = new Car();
carVehicle.Run();
carVehicle = new Porsche911();
carVehicle.Run();
}
@Override
public void Run() {
// TODO Auto-generated method stub
System.out.println("Car Running");
}
}
但是好景不常,某一天客戶提出了新的需求,
他們公司生產了飛天車,這也代表著我們的程式也必須提供飛天車的資料
下面的作法是比較不好的作法
interface Vehicle
{
void Run();
void Fly();
}
class Porsche911 implements Vehicle
{
@Override
public void Run() {
// TODO Auto-generated method stub
System.out.println("Porsche911 Running");
}
@Override
public void Fly() {
// TODO Auto-generated method stub
System.out.println("Porsche911 not Flying");
}
}
class FlyCar implements Vehicle
{
@Override
public void Run() {
// TODO Auto-generated method stub
System.out.println("FlyCar Running");
}
@Override
public void Fly() {
// TODO Auto-generated method stub
System.out.println("FlyCar flying");
}
}
public class Car implements Vehicle{
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Vehicle carVehicle = new Car();
carVehicle.Run();
carVehicle = new Porsche911();
carVehicle.Run();
}
@Override
public void Run() {
// TODO Auto-generated method stub
System.out.println("Car Running");
}
@Override
public void Fly() {
// TODO Auto-generated method stub
System.out.println("Car not Flying");
}
}
可以很清楚的發現,在interface裡新增了 fly這個功能,但是這其實是災難的開始,
這使得原本跑的很正常的 car and Porsh911 這兩個物件也新增了fly的功能,
但是他們並不會飛阿,而且他們也不想要會飛,可是他們現在逼不得以也必須implement Fly這個功能,這樣寫法非常不好的原因在於,
- implement的功能並不是我們所需要的
- 現在只有兩個class需要做修正(Car and Porsh911),當我們有一千個class要修改的時候我相信修改的人一定會......!@#$%
由以上的例子,告訴了我們這是一個不好的設計,然後ISP也提到這就是一個 "胖" interface ,
所以根據上面的例子我們可以做以下的修改
interface Vehicle
{
void Run();
}
interface FlyVehicle extends Vehicle
{
void Fly();
}
class Porsche911 implements Vehicle
{
@Override
public void Run() {
// TODO Auto-generated method stub
System.out.println("Porsche911 Running");
}
}
class FlyCar implements FlyVehicle
{
@Override
public void Run() {
// TODO Auto-generated method stub
System.out.println("FlyCar Running");
}
@Override
public void Fly() {
// TODO Auto-generated method stub
System.out.println("FlyCar flyning");
}
}
public class Car implements Vehicle{
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Vehicle carVehicle = new Car();
carVehicle.Run();
carVehicle = new Porsche911();
carVehicle.Run();
carVehicle = new FlyCar();
((FlyVehicle)carVehicle).Fly();
}
@Override
public void Run() {
// TODO Auto-generated method stub
System.out.println("Car Running");
}
}
我們使用interface FlyVehicle繼承 Vehicle 並新增 fly的功能,
這邊其實跟SingleResponsibilityPrinciple的感覺很像,每個interface都有單一的職責(注意不是單一method)這使得我們只需要修改FlyVehicle就可以了,而且新增功能對於其他的class並不會造成額外的影響。
當然這種修改方式僅限於開發階段,
- 在小怪物還沒有長成千年老妖之前處理是很容易的...
如果是處在維護階段的話,上面的作法可能會牽一髮而動全身,如果能動就動吧,如果不能動就....,試試下面的方法吧
interface Vehicle
{
void Run();
void Fly();
}
abstract class VehicleAdapter implements Vehicle
{
abstract public void Run();
public void Fly()
{
System.out.println("Do Nothing");
}
}
class Porsche911 extends VehicleAdapter
{
public void Run() {
// TODO Auto-generated method stub
System.out.println("Porsche911 Running");
}
}
class FlyCar extends VehicleAdapter
{
@Override
public void Run() {
// TODO Auto-generated method stub
System.out.println("FlyCar Running");
}
@Override
public void Fly() {
// TODO Auto-generated method stub
System.out.println("FlyCar flyning");
}
}
public class Car extends VehicleAdapter{
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Vehicle carVehicle = new Car();
carVehicle.Run();
carVehicle = new Porsche911();
carVehicle.Run();
carVehicle = new FlyCar();
carVehicle.Fly();
}
@Override
public void Run() {
// TODO Auto-generated method stub
System.out.println("Car Running");
}
}
上面使用了一個adapter轉接器將Fly這個功能在adapter中做掉,使得繼承vehicleAdapter的物件不再需要實做fly這個功能,然後Flycar可以override掉原本的功能,改成fly的功能。用這樣的方式就可以使之後想使用vehicle interface但不想fly的人,可以使用新的選擇而不再需要去實做Fly這個功能。
ISP原則感覺就是將SRP原則套在interface身上,希望每個interface都是單一原則(不是單一method),而不是一個interface擁有多個責任,太胖的interface會造成許多問題,
善用這個方法,可以降低耦合,使程式碼更加彈性。