设计模式 #4 (装饰器模式、适配器模式)
文章中所有工程代码和UML
建模文件都在我的这个GitHub
的公开库--->DesignPattern。Star
来一个好吗?秋梨膏!
装饰器模式
简述:在不改变现有对象结构的情况下,为现有对象添加新功能。
需求:玩过那种女孩换装那种游戏吗?什么?没玩过?猛男必玩的呀!现在需要选择套装进行装扮,并计算当前服装的花费。
反例 #1:
public abstract class Suit {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Suit(String name){
this.name =name;
}
abstract int price();
@Override
public String toString() {
return "Suit{" +
"name='" + this.getName() + '\'' + //注意这里使用this.getName()
"price='" + price() + '\'' +
'}';
}
}
public class Suit_01 extends Suit{
public Suit_01() {
super("猛男套装");
}
@Override
public int price() {
return 1899;
}
}
public class Suit_02 extends Suit{
public Suit_02() {
super("青春套装");
}
@Override
public int price() {
return 2199;
}
}
/*=====================客户端=========================*/
public class negtive {
public static void main(String[] args) {
Person girl = new Person("Mary",new suit_02());
System.out.println(girl);
System.out.println(" ");
Person boy = new Person("Jack",new suit_01());
System.out.println(boy);
}
}
这样一看,面向抽象编程,分离的这么好,这是一次不错的设计。
UML
类图如下:
需求:现在游戏出了新服饰,需要为原有的一个人物添加新的服饰进行装扮,要求是不改变原有的装扮。
学习七大设计原则都知道,开闭原则是一个大多数设计模式都遵守的设计原则,此需求要求不改变原有装扮其实就是要求不改变原有对象--Suit
的结构。
-
如果单纯地继承每一个原有套装类
Suit
,重写相关方法进行类的增加,可能会造成代码的臃肿。 -
长期以往随着新增服饰增加,也将导致套装类
Suit
的爆炸式增长。
此时熟悉换装游戏的朋友,不,熟悉设计模式的朋友们就知道要使用装饰器模式进行设计了。
正例 #1:
//抽象服饰类
public abstract class Decorate extends Suit{
protected Suit suit;
public Decorate(Suit suit){
super(suit.getName());
this.suit = suit;
}
}
//服饰——01
public class Canvas_Shoes extends Decorate{
public Canvas_Shoes(Suit suit) {
super(suit);
}
@Override
public String getName() {
return super.getName()+" +帆布鞋";
}
@Override
public int price() {
return suit.price()+399;
}
}
//服饰——02
public class Skirt extends Decorate {
public Skirt(Suit suit) {
super(suit);
}
@Override
public String getName() {
return suit.getName()+" +短裙";
}
@Override
public int price() {
return suit.price()+499;
}
}
/*=================客户端====================*/
public class postive {
public static void main(String[] args) {
Suit_02 girl_suit = new Suit_02();
Skirt skirt = new Skirt(girl_suit);
Canvas_Shoes canvas_shoes = new Canvas_Shoes(skirt);
System.out.println(canvas_shoes);
}
}
UML
类图如下:
这样一来,增加套装类,服饰类都不会违反开闭原则了。虽然还是会产生很多类。
但是客户端需要使用的时候只需要用新增的装饰器类(继承重写Decorate
)嵌套使用进行装饰套裝类(继承重写Suit
)即可。简单点说就是增加新一层外包装--套娃,哈哈。
Java
中,java.io
就是用了装饰器模式进行API
设计:
甚至我们还可以自己写一个加到java.io
中:
public class MyBufferReader extends Reader {
private Reader in;
public MyBufferReader(Reader in){
this.in = in;
}
public String readLine() throws IOException {
StringBuilder sb = new StringBuilder();
int read;
while(true){
read = in.read();
if (read == '\r')
continue;
if (read == '\n' || read == -1){
break;
}
sb.append((char)read);
}
if (sb.toString().length() == 0){
if (read == '\n'){
return "";
}else {
return null;
}
}else {
return sb.toString();
}
}
@Override
public int read(char[] cbuf, int off, int len) throws IOException {
return 0;
}
@Override
public void close() throws IOException {
in.close();
}
/*===================客户端===========================*/
public static void main(String[] args) throws IOException {
Reader in = new FileReader("E:\\1.txt");
MyBufferReader myBufferReader = new MyBufferReader(in);
// BufferedReader br = new BufferedReader(myBufferReader); 可以层层进行套娃包装
String line;
while(( line = myBufferReader.readLine() ) != null){
System.out.println(line);
}
}
}
在查看抽象类Reader
时。可以看到我们自己写的MyBufferReader
已经融入到装饰类的群中,可以进行包装其他Reader
的子类,也可以被其他Reader
子类包装使用。
适配器模式
简述:适配器模式(Adapter),将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
这次不叫反例,叫前提条件。
现在中国球员去NBA
发展,可是初来乍到,会有语言不同的情况,现有设计类如下:
public interface Foreign_Player {
void Speak_Foreign_Language();
String getName();
}
public class CHN_player implements Foreign_Player {
private String name;
public CHN_player(String name) {
this.name =name;
}
public String getName() {
return name;
}
@Override
public void Speak_Foreign_Language() {
System.out.println("用中文说战术。。。。");
}
}
public interface Native_Player {
void speak_English();
String getName();
}
public class NBA_player implements Native_Player {
private String name;
public NBA_player(String name) {
this.name =name;
}
public String getName() {
return name;
}
@Override
public void speak_English() {
System.out.println(name+"Talk about Tactics in English。。。。");
}
}
现在需要让所有球员用英文传达战术,中国球员现学肯定来不及了(不能现改中国球员Foreign_Player
接口违反开闭原则),那就需要一个翻译人员,适配器模式就派上用场了。
public class Adapter_Translator implements Native_Player {
private Foreign_Player foreign_player;
public Adapter_Translator(Foreign_Player foreign_player) {
this.foreign_player = foreign_player;
}
public String getName() {
return foreign_player.getName()+" 的翻译人员";
}
@Override
public void speak_English() {
System.out.println("翻译人员翻译中文战术成英语给队友-------");
}
}
/*=================客户端====================*/
public class postive {
public static void main(String[] args) {
CHN_player yaoming = new CHN_player("yaoming");
System.out.println(yaoming.getName());
yaoming.Speak_Foreign_Language();
//进行适配
Native_Player translator = new Adapter_Translator(yaoming);
System.out.println(translator.getName());
translator.speak_English();
}
}
UML
类图如下:
因为有了翻译人员,中国球员不会因为语言不通的原因导致球员之间无法沟通的问题,这时候,中国球员就好像一个本土球员一样在赛场上打球。
也就是两个实现不同接口的、不存在联系的类,在不修改源代码的前提下,通过适配器,可以实现互通互用,遵守开闭原则。
简单总结一下适配器的编写套路:
-
继承需要适配成的抽象类,或者实现需要适配的接口。(例子中需要适配成
Native_Player
,适配器Adapter_Translator
就实现Native_Player
) -
组合原来不适配的抽象类或者接口。(例子中,适配器
Adapter_Translator
组合了Foreign_Player
接口)-
这里是体现了七大设计原则中的组合优于继承。
-
得此套路,大功即成。