适配器模式
- 作用
将一个类的接口变成客户端所期待的另一种接口,从而时不相匹配的两个类在一起工作。也称为变压器模式、包装模式(包装模式不止这一种)。让两个类可以工作在一起、提高类的复用率、增加透明性、灵活度高。
- 场景
主要用于项目维护阶段
- 示例
代码目录如下:
adapter/
├── sadapter // 新增的适配器代码
│ ├── SecondUserAdapter.java
│ ├── SecondUserAddress.java
│ └── SecondUser.java
├── stable // 已经在运行的代码,不可变
│ ├── FirstUser.java
│ └── IFirstUser.java
├── TestAdapter.java // 测试代码
└── updated // 第三方提供的接口,不可变
├── ISecondUserAddress.java
└── ISecondUser.java
正在运行部分:(stable)
public interface IFirstUser {
void printInfo();
}
public class FirstUser implements IFirstUser {
private String username;
public FirstUser(String username) {
this.username = username;
}
@Override
public void printInfo() {
System.out.println(this.username);
}
}
需求添加部分(updated)
public interface ISecondUser {
void printUsername();
}
public interface ISecondUserAddress {
void printAddress();
}
为此新建适配器,分别新建两个类来实现接口
public class SecondUser implements ISecondUser {
private String username;
public SecondUser(String name) {
this.username = name;
}
@Override
public void printUsername() {
System.out.print(username + " ");
}
}
public class SecondUserAddress implements ISecondUserAddress {
private String addr;
public SecondUserAddress(String address) {
this.addr = address;
}
@Override
public void printAddress() {
System.out.print(this.addr);
}
}
适配器持有两个接口的引用,并实现原有接口
public class SecondUserAdapter implements IFirstUser {
private ISecondUser iSecondUser;
private ISecondUserAddress iSecondUserAddress;
public SecondUserAdapter(ISecondUser iSecondUser, ISecondUserAddress iSecondUserAddress) {
this.iSecondUser = iSecondUser;
this.iSecondUserAddress = iSecondUserAddress;
}
@Override
public void printInfo() {
iSecondUser.printUsername();
iSecondUserAddress.printAddress();
}
}
测试代码
IFirstUser user1 = new FirstUser("User1");
user1.printInfo();
SecondUserAdapter userAdapter =
new SecondUserAdapter(new SecondUser("User2"),new SecondUserAddress("5 street"));
userAdapter.printInfo();
输出
User1
User2 5 street
监听者模式
- 作用
降低对象之间的耦合度,进行解耦,便于模块化开发
当某些数据变化时,某些类会做出相应。处理数据的类主动投送消息,感兴趣的类主动订阅消息
- 场景
Android开发有大量运用,Button控件的点击事件就是一个例子
- 示例
以Android中Button的监听器为例,设计一个接口作为监听器,回调时利用handler控制调用的线程。
private Handler mMainHandler;
// 在主线程中运行
mMainHandler = new Handler(Looper.getMainLooper());
private void notifySthChange(final int state) {
mMainHandler.post(new Runnable() {
@Override
public void run() {
ArrayList<SListener> list = new ArrayList<>(mListeners);
for(SListener l : list) {
l.OnSthChanged(state);
}
}
});
}
在回调中,可以直接更新UI。
桥接模式
- 作用
将抽象和现实解耦,可以独立的变化
- 场景
当一个类内部具备两种或多种变化维度时,使用桥接模式可以解耦这些变化的维度,使高层代码架构稳定
- 示例
模拟工厂生产销售产品
文件目录
bridge/
|-- Client.java // 测试代码
|-- factory
| |-- CanFactory.java // 罐头工厂
| `-- ModernFactory.java // 现代化工厂
|-- Factory.java // 工厂的抽象类
|-- product
| |-- Can.java // 产品具体类 罐头
| `-- Toy.java // 产品具体类 玩具
`-- Product.java // 产品的抽象类
产品类
public abstract class Product {
protected void made() {
System.out.println(getClass().getSimpleName() + " has been made");
}
protected void sold() {
System.out.println(getClass().getSimpleName() + " has been sold");
}
}
public class Can extends Product {
@Override
public void made() {
super.made();
}
@Override
public void sold() {
super.sold();
}
}
public class Toy extends Product {
@Override
public void made() {
super.made();
}
@Override
public void sold() {
super.sold();
}
}
工厂类
public abstract class Factory {
private Product product; // 引用抽象的产品类
public Factory(Product p) {
this.product = p;
}
public void makeMoney() {
product.made();
product.sold();
}
}
public class CanFactory extends Factory {
// 只接受Can类
public CanFactory(Can p) {
super(p);
}
@Override
public void makeMoney() {
super.makeMoney();
System.out.println(getClass().getSimpleName() + " make money!");
}
}
public class ModernFactory extends Factory {
// 只要是Product即可生产
public ModernFactory(Product p) {
super(p);
}
@Override
public void makeMoney() {
super.makeMoney();
System.out.println("ModernFactory make money!");
}
}
罐头工厂CanFactory只能生产罐头Can,而现代工厂ModernFactory可以生产Product,罐头或者玩具都可以。
测试与输出
Can can = new Can();
CanFactory canFactory = new CanFactory(can);
canFactory.makeMoney();
ModernFactory modernFactory = new ModernFactory(new Toy());
modernFactory.makeMoney();
/*
Can has been made
Can has been sold
CanFactory make money!
Toy has been made
Toy has been sold
ModernFactory make money!
*/
构造器模式
- 作用
将复杂对象的构造与其表示分离,以便相同的构造过程可以创建不同的表示。也成为生成器模式。具有较好的封装性【客户端不必知道模型内部细节】、独立易扩展、方便控制风险
- 场景
- 类中有新增属性,需要扩充构造方法,单可选参数过多时,很容易传错参
- 产品类复杂,客户端需要多项配置
- 示例
AOSP中的AlertDialog.Builder
AlertDialog采用了Builder模式,通过builder可以对dialog进行配置。其中,dialog的各项属性可以设置默认值。
public class AlertDialog extends Dialog implements DialogInterface {
public static class Builder {
public Builder(Context context) {}
public Builder setTitle(CharSequence title) {}
public Builder setMessage(@StringRes int messageId) {}
public Builder setPositiveButton(@StringRes int textId, final OnClickListener listener) {}
public Builder setNegativeButton(@StringRes int textId, final OnClickListener listener) {}
public Builder setCancelable(boolean cancelable) {}
// ..................................................
public AlertDialog create() {} //创建dialog
public AlertDialog show() {} // 创建并显示真正的dialog
}
}
JAVA场景下:
public class User implements Serializable {
// ... Member variable\Construction method\Setter\Getter
/**
* User 类建造者类
* @author test
*/
private static class UserBuild {
/**
* 用户编号 required 成员变量
*/
private Integer id ;
/**
* 用户名称 required 成员变量
*/
private String name ;
/**
* 用户年龄 optional 成员变量
*/
private Integer age ;
/**
* 用户身高 optional 成员变量
*/
private Integer height ;
public UserBuild(Integer id ,String name){
this.id = id ;
this.name = name ;
}
public UserBuild age(Integer age){
this.age = age ;
return this ;
}
public UserBuild height(Integer height){
this.height = height ;
return this ;
}
public User build(){
return new User(this);
}
}
public static void main(String[] args) {
User user = new UserBuild(1,"2").age(3).height(4).build();
System.out.println(user.toString());
System.out.println(user.getId());
System.out.println(user.getName());
System.out.println(user.getAge());
System.out.println(user.getHeight());
}
}
命令模式
- 作用
把请求封装成对象,可以在使用不同的请求把客户端参数化。对于请求排队或者记录请求入职提供命令的取消和恢复。这是高内聚。
- 场景
比如餐厅单点,直接将需求封装成命令对象再交给厨师
- 示例
文件格式
command/
|-- cmd
| |-- Command.java // 命令抽象类 - 在这里是战术
| |-- InsideCommand.java // 打内线
| `-- ThreePointCommand.java // 三分战术
|-- Coach.java // 教练
|-- player
| |-- Center.java // 中锋
| |-- Player.java // 运动员抽象类
| |-- PointGuard.java // 控卫
| `-- SmallForward.java // 小前锋
`-- TestMain.java // 测试代码
以篮球训练为例作为示例代码
// 抽象命令
public abstract class Command {
// 可以调用的人员
protected Center center = new Center();
protected PointGuard pontiGurad = new PointForward();
protected SmallForward smallForward = new SmallForward();
public abstract void execute();
}
// 运动员抽象类
public abstract class Playter {
public abstract void run();
public abstract void shoot();
public abstract void passBall();
public abstract void catchBall();
public abstract void dunk();
}
// 教练类
public class Coach {
private Command command;
public void setCommand(Command command){
this.command = command;
}
public void action(){
this.command.execute();
}
}
// 各个人员类
public class Center extends Player {
@Override
public void run() {
System.out.println("Center is running.");
}
@Override
public void shoot() {
System.out.println("Center shoots the ball");
}
// ... 其它命令
}
public class PointGuard extends Player {
@Override
public void run() {
System.out.println("PointGuard is running.");
}
// ...与Center类似
}
public class SmallForward extends Player {
@Override
public void run() {
System.out.println("SmallForward is running.");
}
// ...与Center类似
}
// 以下是简单两个命令
public class InsideCommand extends Command {
public InsideCommand() {
}
@Override
public void execute() {
System.out.println(getClass().getSimpleName() + ":");
super.pointGurad.catchBall();
super.smallForward.run();
// ...
}
}
public class ThreePointCommand extends Command {
// ...与InsideCommand类似
}
// 使用方式
Coach coach = new Coach();
Command command1 = new InsideCommand();
Command command2 = new ThreePointCommand();
coach.setCommand(command1);
coach.action();
装饰者模式
- 作用
动态的给对象添加一些额外的职责。装饰模式比生成子类更加灵活,在不必改变原类文件和使用继承的情况下,动态的扩展一个对象的功能。
- 装饰模式可以是继承关系的替代关系
- 可以扩展一个类
- 但是比较复杂
必须有一个接口或抽象类充当Componet抽象构建
- 场景
- 需要扩展一个类的功能
- 需要为一批的兄弟类进行改装或者假装
- 示例
文件结构
Component // 最基本的抽象构件,接口或抽象类
Operation()
ConcreteComponent // 具体的构件
Decorator // 抽象的装饰者
ConcreteComponent // 具体的装饰者
AWorker作为装饰者,Plumber成为被装饰者。
puiblic class Decoration {
public static void main(String args[]) {
Plumber plumber = new Plumber();
AWorker aWorker = new AWorker(plumber);
aWorker.doSomeWork();
// BWorker同理
}
}
// Worker接口
interface Worker {
void doSomeWork();
}
class Plumber implements Worker {
public void doSomeWork () {
System.out.println("Plumber do some work!");
}
}
class Carpenter implements Worker {
// 与Plumber同理
}
class AWorker implements Worker {
private Worker tempWorker;
public AWorker(Worker worker) {
tempWorker = worker;
}
public void doSomeWork() {
System.out.println("Hello, I am a AWorker");
tempWorker.doSomeWork();
}
}
class BWorker implements Worker {
private Worker worker;
public BWorker(Worker worker) {
this.worker = worker;
}
public void doSomeWork() {
System.out.println("Hello, I am a BWorker");\
worker.doSomeWork();
}
}
策略模式
- 作用
可以定义一种算法,并把每个算法都封装起来,可以互相替换
扩展性好,可以避免多重条件判断
每个策略都是一个类,复用率小
策略类需要对外暴露
场景
- 需要算法屏蔽规则的场景
- 需要算法自由切换的场景
示例
文件结构
strategy/
|-- Add.java // 具体的策略
|-- Division.java // 具体的策略
|-- IStrategy.java // 策略接口
|-- Minus.java // 具体的策略
|-- Paper.java // 测试代码
`-- StrategyContext.java// 上下文角色,承上启下
具体代码
// 策略方法类
public interface IStrategy {
float compute(float a, float b);
}
// 上下文类,用于屏蔽上层模块对策略的直接访问
public class StrategyContext {
private IStrategy strategy;
public void setStrategy(IStrategy strategy) {
this.strategy = startegy;
}
public StrategyContext(IStrategy strategy) {
this.strategy = strategy;
}
public float calculate(float a, float b) {
return this.strategy.compute(a, b);
}
}
// 具体的策略类
public class Add implements IStrategy {
@Override
public float compute(float a, float b) {
return a + b;
}
}
public class Minus implements IStrategy {
// 与Add同理
}
public class Division implements IStrategy {
// 与Add同理
}
// 使用方式
public static void main (String args[]) {
StrategyContext context = new StrategyContext(new Add());
System.out.println("Add Res = " + context.caculate(1,3));
context.setStrategy(new Minus());
System.out.println("Minus Res = " + context.caculate(1,3));
// Division同理
}
也可以用枚举类来封装策略
public enum Calculator {
ADD("+") {
public float exec(float a, float b) {
return a + b;
}
},
SUB("-") {
public float exec(float a, float b) {
return a - b;
}
}
String value;
Calculator(String _value) {
this.value = _value;
}
public String getValue() {
return value;
}
public abstract float exec(float a, float b);
}
// 使用方式
public static void main (String args[]) {
System.out.println("ENUM ADD Res = " + Calculator.ADD.exec(12, 5));
System.out.println("ENUM SUB Res = " + Calculator.SUB.exec(2,5));
}
工厂方法
- 作用
可以首先定义一个用于创建对象的接口。
- 方法创建一个新对象
- 该方法的返回类型为抽象类/接口
- 有若干个类实现了上述抽象模型
具体要实例化什么的类区别于客户代码调用哪一类的工厂方法
- 场景
所有需要生成对象的地方都可以受用,但是要慎重考虑是否需要一个工厂类来管理。
- 示例
iterator()方法就是工厂方法模式的很好例子。
// Iterator接口:
public interface Iterator<E> {
boolean hasNext();
E next();
default void remove() {
throw new UnsupportOperationException("remove");
}
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while(hasNext())
action.accept(next());
}
}
// ArrayList中的iterator()方法返回一个ltr对象
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
// ...
}
}
// LinkedList的iterator()返回的是另一种对象
// LinkerList<E> <-- AbstractSequentialList<E> <-- AbstractList<E>
public ListIterator<E> listIterator() {
return listIterator(0);
}
public ListIterator<E> listIterator(final int index) {
rangeCheckForAdd(index);
return new ListItr(index);
}
private class ListItr extends Itr implements ListIterator<E> {
// ......
}
private class Itr implements Iterator<E> {
// ......
}
---- 后续待更新…