这是软件设计模式系列的最后一篇文章,对于23种设计模式荔枝梳理了比较重要的几种,剩下的在实际开发中用的也比较少哈哈哈。在这篇文章中荔枝主要梳理软件设计模式中的适配器模式、桥梁模式、命令模式和原型模式的相关知识。希望能帮助到有需要的小伙伴呢~~~
前言
一、适配器模式Adapter(Wrapper)
二、桥接模式Bridge
三、命令模式Command
四、原型模式Prototype
总结
适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。就类似于我们笔记本电脑的适配器,把交流电变成可供笔记本电脑使用的直流电,在日常开发中我们经常使用的就是这种设计模式,
package com.crj.adapter;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
public class Main {
public static void main(String[] args) throws Exception {
FileInputStream fis = new FileInputStream("c:/test.text");
InputStreamReader isr = new InputStreamReader(fis);
BufferedReader br = new BufferedReader(isr);
String line = br.readLine();
while (line != null && !line.equals("")) {
System.out.println(line);
}
br.close();
}
}
比如在这段demo中我们要读一个txt文件就必须对对象类型进行转换,比如BufferedReader类实例化需要传入一个InputStreamReader实例对象。这其实就相当于转接,对象类型的转换类似于电源转接。
需要注意的是:常见的Adapt类并不是Adapter,而是为了方便编程给出的接口的抽象实现类,以避免我们继承接口后需要重写所有的接口实现方法。
桥接(Bridge)是用于把抽象化与实现化解耦,使得二者可以独立变化。这种类型的设计模式属于结构型模式,它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。我们可以设想一个场景:交通工具有多种类型,交通工具也有多种颜色,若要创建一个拥有红色法拉利的用户对象,我们是选择重写原来的类型呢还是采用一种别的方式同时使用两种交通工具类?想必小伙伴都会采用后者吧,因为如果采用第一种方式的话可能会导致类爆炸的问题。而桥接模式就能比较好的解决这类需求,桥接模式分离抽象与具体,用聚合方式(桥)连接抽象与具体。
我们首先看看下面demo:
创建一个接口
public interface Draw{
public void drawCircle(int radius, int x, int y);
}
接口的实现类
public class RedCircle implements Draw{
@Override
public void drawCircle(int radius, int x, int y) {
System.out.println("Drawing Circle[ color: red, radius: "
+ radius +", x: " +x+", "+ y +"]");
}
}
class GreenCircle implements Draw {
@Override
public void drawCircle(int radius, int x, int y) {
System.out.println("Drawing Circle[ color: green, radius: "
+ radius +", x: " +x+", "+ y +"]");
}
}
抽象类
public abstract class Shape {
protected Draw draw;
protected Shape(Draw draw){
this.draw = draw;
}
public abstract void draw();
}
抽象类的实现类
public class Circle extends Shape {
private int x, y, radius;
public Circle(int x, int y, int radius, Draw draw) {
super(draw);
this.x = x;
this.y = y;
this.radius = radius;
}
public void draw() {
draw.drawCircle(radius,x,y);
}
}
main类
public class BridgePattern{
public static void main(String[] args) {
Shape redCircle = new Circle(100,100, 10, new RedCircle());
Shape greenCircle = new Circle(100,100, 10, new GreenCircle());
redCircle.draw();
greenCircle.draw();
}
}
demo中的场景是用于画一个圆,我们创建了一个draw接口以及相应的圆的绘制实现类,同时定义一个抽象类及其相应的子类,子类中定义一个构造方法并在draw()中调用了具体的绘制实现类,在main中我们通过在实例化Circle对象的时候传入具体的圆的参数及其调用的绘制实现类实现不同类型的圆的绘制。
命令模式是一种属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。这种模式更多地用在事务Transaction处理的场景,常常将请求类和实现类实现解耦。
命令接口
public interface Order {
void execute();
}
接口实现类
public class BuyStock implements Order {
private Stock abcStock;
public BuyStock(Stock abcStock){
this.abcStock = abcStock;
}
public void execute() {
abcStock.buy();
}
}
class SellStock implements Order {
private Stock abcStock;
public SellStock(Stock abcStock){
this.abcStock = abcStock;
}
public void execute() {
abcStock.sell();
}
}
请求类Stock
请求类中主要定义的是获得请求对象后实体类对象对应调用的方法。
public class Stock {
private String name = "ABC";
private int quantity = 10;
public void buy(){
System.out.println("Stock [ Name: "+name+",
Quantity: " + quantity +" ] bought");
}
public void sell(){
System.out.println("Stock [ Name: "+name+",
Quantity: " + quantity +" ] sold");
}
}
命令调用类
import java.util.ArrayList;
import java.util.List;
public class Broker {
private List orderList = new ArrayList();
public void takeOrder(Order order){
orderList.add(order);
}
public void placeOrders(){
for (Order order : orderList) {
order.execute();
}
orderList.clear();
}
}
main类
public class CommandPatternDemo {
public static void main(String[] args) {
Stock abcStock = new Stock();
BuyStock buyStockOrder = new BuyStock(abcStock);
SellStock sellStockOrder = new SellStock(abcStock);
Broker broker = new Broker();
broker.takeOrder(buyStockOrder);
broker.takeOrder(sellStockOrder);
broker.placeOrders();
}
}
在将请求类和接口实现类进行联系起来的过程中我们需要首先将请求类对象作为构造参数传入接口实现类进行接口实例类对象的实例化,Broker类对象使用命令模式,基于命令的类型确定哪个对象执行哪个命令。
原型模式,又称克隆模式,用于创建重复对象的同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式之一,一般用于一个对象的属性已经确定且需要产生很多相同对象的时候。使用原型模式的时候需要重写Object类下的clone()方法,这是因为Object类下的clone()方法默认是protected的。
需要注意的是:原型模式是Java中自带的,实现原型模式需要实现标记型接口Cloneable,一般会重写clone()方法,如果只是重写clone()方法,而没实现接口,调用时会报异常。
对于克隆模式我们还需要区分深克隆与浅克隆:
浅克隆
浅克隆指的是对该类对象进行复制,复制后的类的对象的引用指向相同的地址。这时候如果被克隆对象p1.loc发生变更,那么p2.loc也会发生变更。
package com.crj.prototype;
/**
* 浅克隆
*/
public class Test {
public static void main(String[] args) throws Exception {
Person p1 = new Person();
Person p2 = (Person)p1.clone();
System.out.println(p2.age + " " + p2.score);
System.out.println(p2.loc);
System.out.println(p1.loc == p2.loc);
p1.loc.street = "sh";
System.out.println(p2.loc);
}
}
class Person implements Cloneable {
int age = 8;
int score = 100;
Location loc = new Location("bj", 22);
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Location {
String street;
int roomNo;
@Override
public String toString() {
return "Location{" +
"street='" + street + '\'' +
", roomNo=" + roomNo +
'}';
}
public Location(String street, int roomNo) {
this.street = street;
this.roomNo = roomNo;
}
}
深克隆
深克隆就是将引用的对象也会被克隆一份,这时候就需要在loc方法后面加上 .clone() 方法。
package com.crj.prototype;
/**
* String需要进一步深克隆吗?
* 不需要!
*/
public class Test {
public static void main(String[] args) throws Exception {
Person p1 = new Person();
Person p2 = (Person)p1.clone();
System.out.println(p2.age + " " + p2.score);
System.out.println(p2.loc);
System.out.println(p1.loc == p2.loc);
p1.loc.street = "sh";
System.out.println(p2.loc);
p1.loc.street.replace("sh", "sz");
System.out.println(p2.loc.street);
}
}
class Person implements Cloneable {
int age = 8;
int score = 100;
Location loc = new Location("bj", 22);
@Override
public Object clone() throws CloneNotSupportedException {
Person p = (Person)super.clone();
p.loc = (Location)loc.clone();
return p;
}
}
class Location implements Cloneable {
String street;
int roomNo;
@Override
public String toString() {
return "Location{" +
"street='" + street + '\'' +
", roomNo=" + roomNo +
'}';
}
public Location(String street, int roomNo) {
this.street = street;
this.roomNo = roomNo;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
注意:
这里的String类型并不需要执行深克隆,这是因为在常量池中,即使我们变更了p1指向的String对象,在常量池中也只是p1指向的String数据对象发生变更,p2指向的对象并不会发生变更。
终于结束第一节阶段的学习,感觉最近两个月学的东西得找时间好好梳理复习一下。最近的知识今天在鹅场面试中被命中好多,但是自己都讲得不是很清楚哈哈哈。除了复盘知识,技术的深度还是需要继续深入学习了,总之继续努力吧~
今朝已然成为过去,明日依然向往未来!我是荔枝,在技术成长之路上与您相伴~~~
如果博文对您有帮助的话,可以给荔枝一键三连嘿,您的支持和鼓励是荔枝最大的动力!
如果博文内容有误,也欢迎各位大佬在下方评论区批评指正!!!