1、设计模式的六大原则
单一职责原则
-
每个类的职责尽量单一。这个在实际项目中很难做到。
开闭原则
-
对扩展开放,对修改关闭。这个原则还是很重要的。
里氏代换原则
-
只要父类能出现的地方子类就可以出现,而且替换为子类也不会产生任何错误或异常,使用者可能根本就不需要知道是父类还是子类。但是,反过来就不行了,有子类出现的地方,父类未必就能适应。这个原则挺不好理解的。
依赖倒转原则
-
针对接口编程,依赖于抽象而不依赖于具体。
接口隔离原则
-
使用多个隔离的接口,比使用单个接口要好。它还有另外一个意思是:降低类之间的耦合度。其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合。
迪米特(最少知道)法则
-
一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。
合成复用原则
-
尽量使用合成/聚合的方式,而不是使用继承。这个原则不属于六大原则,但是十分重要。
良好的代码总是有如下特性:
-
可维护
-
可复用
-
可扩展
-
灵活性高
2、设计模式分类
创建型模式:提供一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 直接实例化对象。 | 工厂模式 抽象工厂模式 单例模式 建造者模式 原型模式 |
结构型模式 | 适配器模式 桥接模式 过滤器模式 组合模式 装饰器模式 外观模式 享元模式 代理模式 |
行为型模式:关注对象之间的通信。 | 责任链模式 命令模式 解释器模式 迭代器模式 中介者模式 备忘录模式 观察者模式 状态模式 空对象模式 策略模式 模板模式 访问者模式 |
J2EE模式:关注表示层。 | MVC 模式 业务代表模式 组合实体模式 数据访问对象模式 前端控制器模式 拦截过滤器模式 服务定位器模式 传输对象模式 |
3、简单工厂模式
简单工厂模式确实挺简单,直接上例子。
首先定义接口。
public interface Animal {
void sayHello();
}
然后定义Animal的实现,Cat、Dog、Pig。
class Cat implements Animal{
@Override
public void sayHello() {
System.out.println("Cat say Hello");
}
}
class Dog implements Animal{
@Override
public void sayHello() {
System.out.println("Dog say Hello");
}
}
class Pig implements Animal{
@Override
public void sayHello() {
System.out.println("Pig say Hello");
}
}
构建一个动物工厂,通过输入不同动物的名字,来获取不同的动物对象。
public class AnimalFactory {
public static Animal getAnimal(String name){
Animal animal = null;
switch (name){
case "cat":
animal = new Cat();
break;
case "dog":
animal = new Dog();
break;
case "pig":
animal = new Pig();
break;
default:
;
}
return animal;
}
}
最后写一个main方法运行。
public class SimpleFactoryTest {
public static void main(String[] args) {
Animal animal = AnimalFactory.getAnimal("dog");
animal.sayHello();
}
}
简单工厂模式不符合开闭原则,为什么呢?如果我想让动物工厂能生产老虎、狮子、大象,那除了增加老虎、狮子、大象类之外,还需要修改AnimalFactory类。
4、策略模式
先不说策略模式的定义,直接来看一个场景吧。
在逛商场的最后支付的时候,商场可能当天在搞活动,比如打折活动、满减活动,也可能没有活动。
根据不同的活动,会有不同的收费方式。比如当天打8折,那么收费方式就是购买商品总价的基础上乘以0.8;如果没有活动,则收费方式是全额。
在这里,收费方式可以当作一种策略。根据老板的要求,收费员可以随意切换收费策略。
说了这么多,再说说策略模式的定义吧:策略模式定义了算法家族,分别封装起来,让它们之间可以互相替换。
根据上述所说的场景,下面就来实现一个策略模式。
首先,定义一个公用的策略接口。
public interface PayStrategy {
/**
* @param price 客户购买商品的总价
* @return 客户最终需要支付的钱
*/
double getResult(double price);
}
然后,定义三类策略,分别是支付全额、打折支付、满减支付。
/**
* 全额支付
*/
public class PayAll implements PayStrategy {
@Override
public double getResult(double price) {
return price;
}
}
/**
* 打折支付
*/
public class PayByRebate implements PayStrategy {
private double rebate;
/**
* 具体打几折
*/
public PayByRebate(double rebate) {
this.rebate = rebate;
}
@Override
public double getResult(double price) {
return price*rebate;
}
}
/**
* 满减支付
*/
public class PayByReturn implements PayStrategy {
private double condition;
private double money;
/**
* 具体满多少减多少
*/
public PayByReturn(double condition, double money) {
this.condition = condition;
this.money = money;
}
@Override
public double getResult(double price) {
if (price >= condition){
return price-money;
}else{
return price;
}
}
}
到此,三种策略类都定义好了,其实程序已经可以工作。但是我们可以想象一个,收费员的电脑上有一个收费页面,该页面上有一个下拉框,该下拉框的选项有正常收费、打八折、满300减100,正好对应上面的三种策略类,但是当收费员选择好收费策略,然后后台去计算需要支付多少钱的时候,难道要去new一个具体的策略类吗?
因为,我们可以将策略模式与简单工厂模式结合起来,再构建一个上下文类。
public class PayContext {
private PayStrategy payStrategy;
public PayContext(String type){
switch (type){
case "正常收费":
payStrategy = new PayAll();
break;
case "打八折":
payStrategy = new PayByRebate(0.8);
break;
case "满300减100":
payStrategy = new PayByReturn(300,100);
break;
default:
;
}
}
public double getResultByStrategy(double price){
return payStrategy.getResult(price);
}
}
细心的读者会发现,上下文类与简单工厂模式中的工厂类还是有点不同的。
工厂类会返回一个父类对象,然后客户端再调用父类对象的方法,这样客户端能感知到父类。
而简单工厂模式+策略模式相结合,父类对象隐藏在上下文对象里,客户端 通过调用上下文对象的getResultByStrategy()方法,返回的是客户端想要的最终结果,而感知不到父类的存在,这样更加降低了客户端与上下文类之间的耦合度。
最后,我们下一个main方法测试一下。
public class StrategyTest {
public static void main(String[] args) {
PayContext pc = new PayContext("满300减100");
double d = pc.getResultByStrategy(500);
System.out.println(d);
}
}
今天的文章就到这里了。设计模式系列文章我更关注实际案例,所以代码会比较多,考虑到文章篇幅,本文只介绍了两种设计模式,下一篇文章会介绍更多的设计模式。