张小帅设计模式实战篇-开篇

背景

这天张小帅对着屏幕上的代码发着呆,心里琢磨着工作了四年依旧是个CRUD男孩。小帅心想着以前学生时代的时候写文章都有套路,为了使得文章表现形式更加丰富,会套用一些成语,一些排比句,比喻句等等,这样写出来的文章才会更加生动。如今工作之后写代码其实和写文章不是差不多么,现在写出来的功能虽然说是实现了,但是索然无味,更加谈不上代码艺术。为了不愿看到以前的自己。小帅于是暗暗发誓,想要改变一下这样的现状。他决定好好再去研究一下设计模式,然后套用到自己日常的代码中,也就有了下面的一些列的张小帅的设计模式学习笔记。

软件架构设计原则

首先一切的一切还是得从软件架构设计原则说起,如果说设计模式春天里绚烂的繁花,那么软件设计原则就是承载绚烂花朵肥沃的泥土,至少张小帅是这么理解的,关于软件设计原则,小帅结合了一些书籍也有了自己的一些理解。

开闭原则

关于开闭原则,比较官方的定义是这样的:指一个软件实体(如类、模块和函数)应该对扩展开放,对修改关闭。从这个定义中其实不难发现,所谓开闭,就是针对的是扩展以及修改两个行为。

小帅是这么理解的就是一个软件实体(无论是类或者其他什么函数)应该通过扩展来实现变化,而不是通过修改已有的代码来实现变化的。

由于小帅老家是开酒厂,于是自然而然地想到了酒。想到酒,如果映射成实体类的话,应该是这样的。

1
2
3
4
5
public interface IWine {
Integer getId(); // 编号
String getName(); // 名称
BigDecimal getPrice(); //售价
}

小帅家的酒厂还是比较大的,另外的品种比较多,并且是自产自销,有白酒,红酒,洋酒,啤酒等,小帅个人比较喜欢啤酒,于是就有了啤酒这个类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Beer implements IWine{
private Integer id;
private String name;
private BigDecimal price;
public Beer(Integer id, String name, BigDecimal price) {
this.id = id;
this.name = name;
this.price = price;
}
public Integer getId() {
return id;
}
public String getName() {
return name;
}
public BigDecimal getPrice() {
return price;
}
}

快临近夏天了,为了能够让啤酒卖得更好,于是小帅家的老爸想要给新上架的张家牌啤酒做一下相关的打8折处理,小帅由于是自家酒厂,所以一向秉承着薄利多销的原则。于是就有了一下这样的扩展。有了关于啤酒打折的类。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class BeerDiscount extends Beer{
public BeerDiscount(Integer id, String name, BigDecimal price) {
super(id, name, price);
}
//原始标价
public BigDecimal getOriginPrice(){
return super.getPrice();
}
//折扣后全新标价
public BigDecimal getPrice(){
return super.getPrice().multiply(new BigDecimal(0.8));
}
}

简单看一下开闭原则的类图如下:

开闭原则类图

依赖倒置原则

依赖倒置原则是指设计代码结构时,高层模块不应该依赖底层模块,二者应该依赖其抽象。通过依赖倒置,可以减少类和类之间的耦合性。

我们还是接着小帅家是开酒厂以及卖酒的这个话题继续,除了开酒厂以及卖酒之外,小帅的父亲大帅也比较喜欢喝酒,当然也不是酗酒。父亲大帅喜欢和白酒以及黄酒。于是就有了下面这样的代码。

1
2
3
4
5
6
7
8
public class DS {
public void drinkWhiteSpirit(){
System.out.println("大帅喜欢喝白酒");
}
public void drinkRiceWine(){
System.out.println("大帅喜欢喝黄酒");
}
}

我们来看一下这个时候的调用方式:

1
2
3
4
5
 public static void main(String[] args) {
DS ds = new DS();
ds.drinkRiceWine();
ds.drinkWhiteSpirit();
}

夏天快要来了,小帅的父亲又喜欢喝上了啤酒,于是在原始的逻辑上面就需要在加上一个方法,然后重新去进行调用,显然,这种方式是比较非常不稳定,在修改原始代码的时候也会带来其他的风险。因此,我们需要对酒进行抽象。

1
2
3
public interface Wine {
void drink();
}

分别实现喝黄酒以及喝啤酒

1
2
3
4
5
public class RiceWine implements Wine{
public void drink() {
System.out.println("喜欢喝黄酒");
}
}
1
2
3
4
5
public class Beer implements Wine{
public void drink() {
System.out.println("喜欢喝啤酒");
}
}

此时我们将大帅的喝酒行为的代码进行一下调整

1
2
3
4
5
6
7
8
9
10
11
12
13
public class DS2 {
IWine wine;
public DS2 (IWine wine){
this.wine = wine;
}
public void drink(){
wine.drink();
}
public static void main(String[] args) {
DS2 ds2 = new DS2(new RiceWine());
ds2.drink();
}
}

以这种方式进行调用并且“喝酒”的话,首先会比较灵活,第二个随着业务的扩展也无需去更新老代码的逻辑,扩展性也比较强,如果大帅有一天要喝其他酒水的时候,直接实现喝酒的接口即可,无需动原始的接口数据,当然这种每次调用还是需要去创建DS2对象,如果是全局变量的话,我们可以再进行改造一下,通过set方式去传递行为,具体如下。

1
2
3
4
5
6
7
8
9
public class DS2_copy {
IWine wine;
public void setWine(IWine wine) {
this.wine = wine;
}
public void drink(){
wine.drink();
}
}

以抽象为基准比以细节为基准搭建起来的架构要稳定的多,因此拿到需求之后,要面向接口编程,先顶层再到细节设计。

以上最终实现的代码结构图如下所示:

开闭原则类图

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×