这一篇主要讲结构型模式的后面四种
结构型模式旨在通过改变代码结构来达到解耦的目的,使得我们的代码容易维护和扩展。
装饰模式
component是逻辑实现类,decorator作为装饰器,用来增强component方法,像是添加小功能等
例子代码是奶茶和加调料
先声明奶茶接口,拥有描述和价格等属性
public abstract class Beverage {
// 返回描述
public abstract String getDescription();
// 返回价格
public abstract double cost();
}
实现红茶和绿茶
public class BlackTea extends Beverage {
public String getDescription() {
return "红茶";
}
public double cost() {
return 10;
}
}
public class GreenTea extends Beverage {
public String getDescription() {
return "绿茶";
}
public double cost() {
return 11;
}
}
定义调料接口,这里调料其实就是装饰器decorator
// 调料
public abstract class Condiment extends Beverage {}
定义两种调料,这里的关键是注入饮料类,方法内简单的累加
public class Lemon extends Condiment {
private Beverage beverage;
// 这里很关键,需要传入具体的饮料,如需要传入没有被装饰的红茶或绿茶
public Lemon(Beverage beverage) {
this.beverage = beverage;
}
public String getDescription() {
// 装饰
return beverage.getDescription() + ", 加柠檬";
}
public double cost() {
// 装饰
return beverage.cost() + 2; // 加柠檬需要 2 元
}
}
public class Mango extends Condiment {
private Beverage beverage;
public Mango(Beverage beverage) {
this.beverage = beverage;
}
public String getDescription() {
return beverage.getDescription() + ", 加芒果";
}
public double cost() {
return beverage.cost() + 3;
}
}
使用,选择绿茶,芒果和柠檬作为装饰
public static void main(String[] args) {
// 首先,我们需要一个基础饮料,红茶、绿茶或咖啡
Beverage beverage = new GreenTea();
// 开始装饰
beverage = new Lemon(beverage); // 先加一份柠檬
beverage = new Mango(beverage); // 再加一份芒果
// 如果我们需要 芒果-双份柠檬-红茶
// Beverage beverage = new Mango(new Lemon(new Lemon(new BlackTea())));
System.out.println(beverage.getDescription() + " 价格:¥" + beverage.cost());
//"绿茶, 加柠檬, 加芒果 价格:¥16"
}
可以加很多装饰
整体结构如图
优缺点:
- 优点是使用灵活,装饰类的排列组合可以实现不同效果
- 缺点是需要很多注入很多装饰子类去修饰或增强逻辑
使用场景:java IO就是用到了装饰模式
DataInputStream is = new DataInputStream(
new BufferedInputStream(
new FileInputStream("")));
个人理解和桥接模式有点像,桥接模式的定义是将抽象化与实现化分离(用组合的方式而不是继承的方式)使得两者可以独立变化。可以减少派生类的增长。这里总结下区别:
- 桥接模式中所说的分离,其实是指将结构与实现分离(当结构和实现有可能发生变化时)或属性与基于属性的行为进行分离;而装饰者只是对基于属性的行为进行封闭成独立的类。
- 桥接中的行为是横向的行为,行为彼此之间无关联;而装饰者模式中的行为具有可叠加性,其表现出来的结果是一个整体,一个各个行为组合后的一个结果。
外观模式(门面模式)
在日常编码工作中,我们都在有意无意的大量使用外观模式。其实很好理解,直接上代码
先定义图形接口,三种图形的实现:
public interface Shape {
void draw();
}
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Circle::draw()");
}
}
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Rectangle::draw()");
}
}
public class Square implements Shape {
@Override
public void draw() {
System.out.println("Square::draw()");
}
}
外观模式直接实例化所有的图形类
public class ShapeMaker {
private Shape circle;
private Shape rectangle;
private Shape square;
public ShapeMaker() {
circle = new Circle();
rectangle = new Rectangle();
square = new Square();
}
/**
* 下面定义一堆方法,具体应该调用什么方法,由这个门面来决定
*/
public void drawCircle(){
circle.draw();
}
public void drawRectangle(){
rectangle.draw();
}
public void drawSquare(){
square.draw();
}
}
想要哪个类的方法直接取就行了
public static void main(String[] args) {
ShapeMaker shapeMaker = new ShapeMaker();
shapeMaker.drawCircle();
shapeMaker.drawRectangle();
shapeMaker.drawSquare();
}
现阶段各种第三方SDK、开源类库,很大概率都会使用外观模式。slf4j就用到了外观去实现logback/log4j等不同的日志框架
组合模式
类中通过 add(node)、remove(node)等方法修改节点,员工/部门架构图很适合用组合模式
public class Employee {
private String name;
private String dept;
private int salary;
private List<Employee> subordinates; // 下属
public Employee(String name,String dept, int sal) {
this.name = name;
this.dept = dept;
this.salary = sal;
subordinates = new ArrayList<Employee>();
}
public void add(Employee e) {
subordinates.add(e);
}
public void remove(Employee e) {
subordinates.remove(e);
}
public List<Employee> getSubordinates(){
return subordinates;
}
public String toString(){
return ("Employee :[ Name : " + name + ", dept : " + dept + ", salary :" + salary+" ]");
}
}
树结构就是用到了组合模式
这里把Composite作为树枝,Leaf作为叶,不管是树枝还是叶本质上都是Component
public class CompositePattern {
public static void main(String[] args) {
Component c0 = new Composite();
Component c1 = new Composite();
Component leaf1 = new Leaf("1");
Component leaf2 = new Leaf("2");
Component leaf3 = new Leaf("3");
c0.add(leaf1);
c0.add(c1);
c1.add(leaf2);
c1.add(leaf3);
c0.operation();
}
}
//抽象构件
interface Component {
public void add(Component c);
public void remove(Component c);
public Component getChild(int i);
public void operation();
}
//树叶构件
class Leaf implements Component {
private String name;
public Leaf(String name) {
this.name = name;
}
public void add(Component c) {
}
public void remove(Component c) {
}
public Component getChild(int i) {
return null;
}
public void operation() {
System.out.println("树叶" + name + ":被访问!");
}
}
//树枝构件
class Composite implements Component {
private ArrayList<Component> children = new ArrayList<Component>();
public void add(Component c) {
children.add(c);
}
public void remove(Component c) {
children.remove(c);
}
public Component getChild(int i) {
return children.get(i);
}
public void operation() {
for (Object obj : children) {
((Component) obj).operation();
}
}
}
享元模式
享元(Flyweight)模式的定义:运用共享技术来复用已经生成的对象
先定义接口和实现
//接口
interface Flyweight {
public void operation(UnsharedConcreteFlyweight state);
}
//具体实现
class ConcreteFlyweight implements Flyweight {
private String key;
ConcreteFlyweight(String key) {
this.key = key;
System.out.println("具体享元" + key + "被创建!");
}
public void operation(UnsharedConcreteFlyweight outState) {
System.out.print("具体享元" + key + "被调用,");
System.out.println("非享元信息是:" + outState.getInfo());
}
}
该类统一管理Flyweight
//享元工厂角色
class FlyweightFactory {
private HashMap<String, Flyweight> flyweights = new HashMap<String, Flyweight>();
public Flyweight getFlyweight(String key) {
Flyweight flyweight = (Flyweight) flyweights.get(key);
if (flyweight != null) {
System.out.println("具体享元" + key + "已经存在,被成功获取!");
} else {
flyweight = new ConcreteFlyweight(key);
flyweights.put(key, flyweight);
}
return flyweight;
}
}
写一个非享元类
//非享元角色
class UnsharedConcreteFlyweight {
private String info;
UnsharedConcreteFlyweight(String info) {
this.info = info;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
}
使用
public static void main(String[] args) {
FlyweightFactory factory = new FlyweightFactory();
Flyweight f01 = factory.getFlyweight("a");
Flyweight f02 = factory.getFlyweight("a");
Flyweight f03 = factory.getFlyweight("a");
Flyweight f11 = factory.getFlyweight("b");
Flyweight f12 = factory.getFlyweight("b");
f01.operation(new UnsharedConcreteFlyweight("第1次调用a。"));
f02.operation(new UnsharedConcreteFlyweight("第2次调用a。"));
f03.operation(new UnsharedConcreteFlyweight("第3次调用a。"));
f11.operation(new UnsharedConcreteFlyweight("第1次调用b。"));
f12.operation(new UnsharedConcreteFlyweight("第2次调用b。"));
}
优点:相同对象只要保存一份,这降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力。
使用场景:在特定的场景中缓存已经创建的对象,用于提高性能。
最简单的例子:用一个 HashMap 来存放新生成的对象。每次需要一个对象时,先到 HashMap 中看看有没有,如果没有,再生成新的对象,然后将这个对象放入 HashMap 中。