现在对不同手机类型的不同品牌实现操作编程(比如:开机、关机、上网,打电话等),如图
【对应类图】
【分析】
类的最小设计原则
(实现功能的同时,让类尽可能少),通过使用封装、聚合及继承等行为让不同的类承担不同的职责。它的主要特点是把抽象(Abstraction)与行为实现(lmplementation)分离开来,从而可以保持各部分的独立性以及应对他们的功能扩展假设现在有一个类Something。当我们想在Something中增加新功能时(想增加一个具体方法时),会编写一个Something类的子类(派生类),如SomethingGood类。这样就构成了一个小小的类层次结构。
父类本身具备一些基本功能,子类在继承父类的功能之外,还可以添加新的功能。这种层次结构被称为“类的功能层次结构”。如果需要在SomethingGood的基础上继续增加新的功能,再写SomethingGood的子类即可。
注意:类的层次结构关系不应当过深
抽象类声明了一些抽象方法,定义了接口(API),然后子类负责去实现这些抽象方法。父类的任务是通过声明抽象方法的方式定义接口(API),而子类的任务是实现抽象方法。正是由于父类和子类的这种任务分担,我们才可以编写出具有高可替换性的类。
当子类Concreteclass实现了父类Abstractclass类的抽象方法时,它们之间就构成了一个小小的层次结构。
这种类的层次结构(类的实现层次结构)并非用于增加功能,并不方便我们增加新的方法。它的真正作用是帮助我们实现下面这样的任务分担
一个抽象类可以有多种子实现类
当类的层次结构只有一层时,功能层次结构与实现层次结构是混杂在一个层次结构中的。这样很容易使类的层次结构变得复杂,也难以透彻地理解类的层次结构。因为自己难以确定究竟应该在类的哪一个层次结构中去增加子类。
因此,我们需要将“类的功能层次结构”与“类的实现层次结构”分离为两个独立的类层次结构。当然,如果只是简单地将它们分开,两者之间必然会缺少联系。所以我们还需要在它们之间搭建一座桥梁,这就是桥接模式。
抽象类和接口是聚合关系,也是调用和被调用关系
对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用,如以下场景:
【接口】
package com.test.bridge;
/**
* 接口
*/
public interface Brand {
void open();
void close();
void call();
}
【接口实现类:小米手机】
package com.test.bridge;
public class XiaoMi implements Brand {
@Override
public void open() {
System.out.println(" 小米手机开机 ");
}
@Override
public void close() {
System.out.println(" 小米手机关机 ");
}
@Override
public void call() {
System.out.println(" 小米手机打电话 ");
}
}
【接口实现类:Vivo手机】
package com.test.bridge;
public class Vivo implements Brand {
@Override
public void open() {
System.out.println(" Vivo手机开机 ");
}
@Override
public void close() {
System.out.println(" Vivo手机关机 ");
}
@Override
public void call() {
System.out.println(" Vivo手机打电话 ");
}
}
【抽象类】
package com.test.bridge;
public abstract class Phone {
/**
* 聚合品牌
*/
private Brand brand;
/**
* 构造器
* @param brand
*/
public Phone(Brand brand) {
super();
this.brand = brand;
}
protected void open() {
this.brand.open();
}
protected void close() {
this.brand.close();
}
protected void call() {
this.brand.call();
}
}
【抽象类子类:折叠手机】
package com.test.bridge;
/**
* 折叠式手机类,继承 抽象类 Phone
*/
public class FoldPhone extends Phone {
//构造器
public FoldPhone(Brand brand) {
super(brand);
}
public void open() {
System.out.println(" 折叠样式手机 ");
// 实际上调用的是具体品牌(如Xiaomi)的开机方法,抽象类Phone充当桥接作用
super.open();
}
public void close() {
System.out.println(" 折叠样式手机 ");
super.close();
}
public void call() {
System.out.println(" 折叠样式手机 ");
super.call();
}
}
【抽象类子类:直立手机】
package com.test.bridge;
public class UpRightPhone extends Phone {
//构造器
public UpRightPhone(Brand brand) {
super(brand);
}
public void open() {
System.out.println(" 直立样式手机 ");
super.open();
}
public void close() {
System.out.println(" 直立样式手机 ");
super.close();
}
public void call() {
System.out.println(" 直立样式手机 ");
super.call();
}
}
【客户端】
package com.test.bridge;
public class Client {
public static void main(String[] args) {
//获取折叠式手机 (样式 + 品牌 才是具体的手机)
Phone phone1 = new FoldPhone(new XiaoMi());
phone1.open();
phone1.call();
phone1.close();
System.out.println("==============");
Phone phone2 = new FoldPhone(new Vivo());
phone2.open();
phone2.call();
phone2.close();
System.out.println("==============");
UpRightPhone phone3 = new UpRightPhone(new XiaoMi());
phone3.open();
phone3.call();
phone3.close();
System.out.println("==============");
UpRightPhone phone4 = new UpRightPhone(new Vivo());
phone4.open();
phone4.call();
phone4.close();
}
}
【运行】
折叠样式手机
小米手机开机
折叠样式手机
小米手机打电话
折叠样式手机
小米手机关机
==============
折叠样式手机
Vivo手机开机
折叠样式手机
Vivo手机打电话
折叠样式手机
Vivo手机关机
==============
直立样式手机
小米手机开机
直立样式手机
小米手机打电话
直立样式手机
小米手机关机
==============
直立样式手机
Vivo手机开机
直立样式手机
Vivo手机打电话
直立样式手机
Vivo手机关机
Process finished with exit code 0
Display使用DispalyImpl的方法来完成功能,StringDisplayImpl负责DispalyImpl方法的具体实现,CountDisplay用来完成更多的功能
【类的功能层次结构:Display】
package com.test.bridge.Sample;
/**
* 类的功能层次最上层
*/
public class Display {
/**
* 实现了Display类的具体功能的实例 (桥梁)
*/
private DisplayImpl impl;
public Display(DisplayImpl impl) {
this.impl = impl;
}
/**
* 显示前的处理
*/
public void open() {
impl.rawOpen();
}
/**
* 显示处理
*/
public void print() {
impl.rawPrint();
}
/**
* 显示后的处理
*/
public void close() {
impl.rawClose();
}
/**
* 调用上面的三个方法来进行显示
*/
public final void display() {
open();
print();
close();
}
}
private DisplayImpl impl;
使用了“委托”关系,而不是使用“继承”关系。继承是强关联关系,委托是弱关联关系。使用委托更方便扩展。
【类的功能层次结构:CountDisplay】
package com.test.bridge.Sample;
/**
* 在Display类的基础上增加新功能
*/
public class CountDisplay extends Display {
public CountDisplay(DisplayImpl impl) {
super(impl);
}
/**
* 循环显示times次
*
* @param times
*/
public void multiDisplay(int times) {
open();
for (int i = 0; i < times; i++) {
print();
}
close();
}
}
【类的实现层次结构:上层】
package com.test.bridge.Sample;
/**
* 类的实现层次最上层
*/
public abstract class DisplayImpl {
public abstract void rawOpen();
public abstract void rawPrint();
public abstract void rawClose();
}
【类的实现层次结构:下层】
package com.test.bridge.Sample;
/**
* 类的实现层次结构,具体实现类
*/
public class StringDisplayImpl extends DisplayImpl {
/**
* 要显示的字符串
*/
private String string;
/**
* 以字节单位计算出的字符串的宽度
*/
private int width;
/**
* 构造方法
*
* @param string 构造函数接收要显示的字符串string
*/
public StringDisplayImpl(String string) {
// 将它保存在字段中
this.string = string;
// 把字符串的宽度也保存在字段中,以供使用
this.width = string.getBytes().length;
}
public void rawOpen() {
printLine();
}
public void rawPrint() {
// 前后加上"|"并显示
System.out.println("|" + string + "|");
}
public void rawClose() {
printLine();
}
private void printLine() {
// 显示用来表示方框的角的"+"
System.out.print("+");
// 显示width个"-"
for (int i = 0; i < width; i++) {
// 将其用作方框的边框
System.out.print("-");
}
// 显示用来表示方框的角的"+"
System.out.println("+");
}
}
【主类】
package com.test.bridge.Sample;
public class Main {
public static void main(String[] args) {
Display d1 = new Display(new StringDisplayImpl("Hello, China."));
Display d2 = new CountDisplay(new StringDisplayImpl("Hello, World."));
CountDisplay d3 = new CountDisplay(new StringDisplayImpl("Hello, Universe."));
d1.display();
System.out.println();
d2.display();
System.out.println();
d3.display();
d3.multiDisplay(5);
}
}
【运行】
+-------------+
|Hello, China.|
+-------------+
+-------------+
|Hello, World.|
+-------------+
+----------------+
|Hello, Universe.|
+----------------+
+----------------+
|Hello, Universe.|
|Hello, Universe.|
|Hello, Universe.|
|Hello, Universe.|
|Hello, Universe.|
+----------------+
Process finished with exit code 0
在上述示例程序中增加一个类,实现“显示字符串若干(随机)次”的功能。
【新增类】
package com.test.bridge.A1;
import java.util.Random;
public class RandomCountDisplay extends CountDisplay {
private Random random = new Random();
public RandomCountDisplay(DisplayImpl impl) {
super(impl);
}
public void randomDisplay(int times) {
multiDisplay(random.nextInt(times));
}
}
【主类】
package com.test.bridge.A1;
public class Main {
public static void main(String[] args) {
RandomCountDisplay d = new RandomCountDisplay(new StringDisplayImpl("Hello, China."));
d.randomDisplay(10);
}
}
在上述示例程序中增加一个类,实现“显示文本文件的内容”的功能。
【新增类】
package com.test.bridge.A2;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class FileDisplayImpl extends DisplayImpl {
private String filename;
private BufferedReader reader;
/**
* 循环显示的极限(缓存大小限制)
*/
private final int MAX_READAHEAD_LIMIT = 4096;
public FileDisplayImpl(String filename) {
this.filename = filename;
}
public void rawOpen() {
try {
reader = new BufferedReader(new FileReader(filename));
reader.mark(MAX_READAHEAD_LIMIT);
} catch (IOException e) {
e.printStackTrace();
}
// 装饰框
System.out.println("=-=-=-=-=-= " + filename + " =-=-=-=-=-=");
}
public void rawPrint() {
try {
String line;
reader.reset(); // 回到mark的位置
while ((line = reader.readLine()) != null) {
System.out.println("> " + line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void rawClose() {
// 装饰框
System.out.println("=-=-=-=-=-= ");
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
【主类】
package com.test.bridge.A2;
public class Main {
public static void main(String[] args) {
CountDisplay d = new CountDisplay(new FileDisplayImpl("star.txt"));
d.multiDisplay(3);
}
}
类的功能层次结构
的最上层。它使用Implementor角色的方法定义了基本的功能。该角色中保存了Implementor 角色的实例提高了系统的灵活性
,让抽象部分和实现部分独立开来,这有助于系统进行分层设计,从而产生更好的结构化系统桥接模式替代多层继承方案
,可以减少子类的个数,降低系统的管理和维护成本