关于五种设计模式的分享
设计模式是指在开发过程中积累出的久经考验且能用于解决在特定环境下、重复出现的、特定问题的解决方案。
工厂模式
分为简单、普通、抽象三种具体模式,本次仅分析鉴定简单工厂与普通工厂。
工厂方法模式的特点,是定义一个用于创建对象的接口,让子类决定实例化哪一个具体类。工厂方法使一个类的实例化延迟到其子类。
使用场景:需要创建复杂对象时,适合于数据库、日志系统等。简单到能够随便new着用的对象就没必要使用工厂模式了。
优点:去除了消费者与实体间的直接依赖,同时可以在消费者无感的情况下对创建的对象进行 “加工” 。
弊端:增加了代码量,在某种程度上使项目变臃肿。
public interface Clothes {
/**
* 制造衣物
*/
void make();
}
/**
* 裙子
*/
class Dress implements Clothes {
@Override
public void make() {
System.out.println("给你裙子");
}
}
/**
* 衬衫
*/
class Shirt implements Clothes {
@Override
public void make() {
System.out.println("给你衬衫");
}
}
/**
* 裤子
*/
class Pants implements Clothes {
@Override
public void make() {
System.out.println("给你裤子");
}
}
/**
* 没造出来
*/
class NoClothes implements Clothes {
@Override
public void make() {
System.out.println("没造出来");
}
}
-
简单工厂:
具体的功能类需要实现相同的接口,由工厂负责进行逻辑判断与实例化。
消费者不关心工厂的内部操作,仅提出需求,实例化由工厂处理,并返回符合消费者需要的对应实例。
class SimpleFactory { Clothes makeClothes(String type) { Clothes clothes; switch (type) { case "dress": clothes = new Dress(); break; case "shirt": clothes = new Shirt(); break; case "pants": clothes = new Pants(); break; default: clothes = new NoClothes(); break; } return clothes; } } public class SimpleFactoryDemo { public static void main(String[] args) { Clothes clothes; System.out.println("简单工厂模式:"); SimpleFactory simpleFactory = new SimpleFactory(); System.out.println("工厂,我需要裤子"); clothes = simpleFactory.makeClothes("pants"); clothes.make(); System.out.println("工厂,我需要衬衫"); clothes = simpleFactory.makeClothes("shirt"); clothes.make(); } }
优势:消费者使用方便,消费者端修改功能只需要更改传入的参数名即可,增加功能或修改功能时无需修改消费者代码,只需在工厂中添加对应的判断分支。
弊端:当需创建的对象多时,工厂类的代码量会相当高,同时每次维护都需要对工厂类进行修改,导致工厂类耦合度上升,同时违反了开闭原则。
-
普通工厂:
在具体功能类实现相同接口的基础上,所有工厂类继承同一个抽象的构造类。将简单工厂中的逻辑判断迁移到了客户端,由消费者自己选择要使用的工厂,而不同的工厂负责创建不同的具体功能类,即工厂与功能类一一对应。
/** * 抽象工厂类 */ abstract class AbstractFactory { /** * 抽象获取实体的方法 * * @return 衣物实体 */ abstract Clothes getClothes(); } /** * 衬衫工厂 */ class ShirtFactory extends AbstractFactory { @Override Clothes getClothes() { System.out.println("衬衫工厂开工"); return new Shirt(); } } /** * 裙子工厂 */ class DressFactory extends AbstractFactory { @Override Clothes getClothes() { System.out.println("裙子工厂开工"); return new Dress(); } } public class NormalFactoryDemo { public static void main(String[] args) { AbstractFactory factory; System.out.println("工厂模式:"); System.out.println("想要裙子,来个裙子工厂造一下"); factory = new DressFactory(); factory.getClothes().make(); System.out.println("想要衬衫,来个衬衫工厂造一下"); factory = new PantsFactory(); factory.getClothes().make(); } }
优势:降低了工厂的耦合度,克服了简单工厂违反开闭原则的缺点。
弊端:维护或增加功能时,需要修改的代码量增加。
组合模式
组合模式是将多个对象组合成树形结构来表现”部分-整体“的层次结构,使得消费者可以按处理单个对象的方式对待多个对象的组合。
组合模式实现的最关键的地方是互通的接口。
基础对象和组装起来的复杂对象之间有相同的接口,这就是组合模式能够将组合对象和简单对象进行一致处理的原因。
使用场景:树形菜单、树形文件夹管理等
/**
* 抽象构件
*/
abstract class AbstractComponent {
String mark;
AbstractComponent(String mark) {
this.mark = mark;
}
/**
* 添加子节点
*/
abstract void add(AbstractComponent component);
/**
* 删除子节点
*/
abstract void remove(AbstractComponent component);
/**
* 显示结构
*/
abstract void function(int depth);
}
/**
* 构件类型1:分支型
* 分支型特有:实现添加与删除节点功能
* 同时实现方法功能
*/
class Branch extends AbstractComponent {
Branch(String mark) {
super(mark);
}
private List<AbstractComponent> components = new ArrayList<>();
@Override
void add(AbstractComponent component) {
components.add(component);
}
@Override
void remove(AbstractComponent component) {
components.remove(component);
}
@Override
void function(int depth) {
for (int i = 1; i < depth; i++) {
System.out.print("--");
}
System.out.println(mark);
for (AbstractComponent component : components
) {
component.function(depth + 1);
}
}
}
/**
* 构件类型2:末端型
* 仅实现方法功能
*/
class Extremity extends AbstractComponent {
Extremity(String mark) {
super(mark);
}
@Override
void add(AbstractComponent component) {
}
@Override
void remove(AbstractComponent component) {
}
@Override
void function(int depth) {
for (int i = 1; i < depth; i++) {
System.out.print("--");
}
System.out.println(mark);
}
}
public class CompositeDemo {
public static void main(String[] args) {
System.out.println("设计模式-组合模式Demo:
");
//初始化根节点
AbstractComponent root = new Branch("根节点构件");
//配置分支
AbstractComponent branch1 = new Branch("分支构件1");
branch1.add(new Extremity("末端构件1-1"));
AbstractComponent branch2 = new Branch("分支构件2");
AbstractComponent branch21 = new Branch("分支构件2-1");
branch21.add(new Extremity("末端构件2-1-1"));
branch2.add(branch21);
AbstractComponent branch22 = new Branch("分支构件2-2");
branch22.add(new Extremity("末端构件2-2-1"));
branch22.add(new Extremity("末端构件2-2-2"));
branch2.add(branch22);
//组合构件
root.add(branch1);
root.add(branch2);
root.add(new Branch("分支构件3"));
root.add(new Extremity("末端构件3"));
//使用构件
root.function(1);
}
}
优势:
- 组合模式使得消费者可以一致地处理对象和对象容器,无需关心处理的是单个对象还是组合的对象容器。
- 使消费者不用直接依赖复杂的对象容器,降低耦合度。
- 可以更容易地往组合对象中加入新的构件。
弊端: 使得设计更加复杂。消费者需要花更多时间理清类之间的层次关系。
装饰器模式
装饰器模式的特点是允许向一个现有的对象添加新的功能,同时又不改变其结构。
这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
使用场景:旧代码维护、动态为对象添加功能
/**
* 抽象构件,装饰器和基础构件都需要继承它
*/
abstract class AbstractComponent {
/**
* 功能
*/
abstract void function();
}
/**
* 基础构件
*/
class BaseComponent extends AbstractComponent {
@Override
void function() {
System.out.println("这里是基本类在小声说话");
}
}
/**
* 装饰器构件
*/
class Decorator extends AbstractComponent {
private AbstractComponent component;
Decorator(AbstractComponent component) {
this.component = component;
}
@Override
void function() {
component.function();
speakLoudly();
}
void speakLoudly() {
System.out.println("然后借助装饰器又喊了一遍");
}
}
public class DecoratorDemo {
public static void main(String[] args) {
AbstractComponent baseComponent = new BaseComponent();
AbstractComponent superComponent = new Decorator(baseComponent);
System.out.println("基本类:");
baseComponent.function();
System.out.println("装饰器增强:");
superComponent.function();
}
}
优势:
- 动态扩展类功能,比类继承灵活,且对消费者透明。
- 可以对同一个被装饰对象进行多次装饰,创建出不同行为的复合功能。
弊端:
- 多层装饰器的代码比较复杂。
- 因为每个装饰层都会创建一个相应的对象,当装饰嵌套过多时,会产生过多小对象。
- 装饰嵌套会导致易于出错,且调试排查复杂度高。
桥接模式
桥接模式是将抽象部分与它的实现部分分离,使它们都可以独立地变化。
为对象提供多个维度的变化而又不引入额外的复杂度。
程序跑着跑着遇到一座桥,桥接到哪里就去哪里。
使用场景:需要在构件间有足够高灵活性的系统、运行时需要动态切换对象的系统、不希望多层级继承的系统。
/**
* 被桥接的类应该实现这个接口
*/
interface Bridgeable {
/**
* 功能接口
*/
public void method();
}
/**
* 跑步模式
*/
class RunningBridge implements Bridgeable {
@Override
public void method() {
System.out.println("连接跑步端口
一直在跑步...");
}
}
/**
* 站定模式
*/
class StandingBridge implements Bridgeable {
@Override
public void method() {
System.out.println("连接原地呆着端口
站在原地不动...");
}
}
/**
* 定义一个抽象类桥,定义桥接目标的抽象实例
*/
abstract class AbstractBridge {
/**
* 待放入的桥接目标
*/
private Bridgeable bridgeable;
/**
* 使用桥接目标的方法
*/
public void method() {
bridgeable.method();
}
/**
* 设置桥接的目标
*/
void setBridgeable(Bridgeable bridgeable) {
this.bridgeable = bridgeable;
}
/**
* 获取桥接的目标
*/
Bridgeable getBridgeable() {
return bridgeable;
}
}
/**
* 实现功能的桥
*/
class MyBridge extends AbstractBridge {
@Override
public void method() {
getBridgeable().method();
}
}
public class BridgeDemo {
public static void main(String[] args) {
// 实例化一个桥
AbstractBridge bridge = new MyBridge();
// 实例化一个桥接目标
RunningBridge runningBridge = new RunningBridge();
// 把目标传入桥
bridge.setBridgeable(runningBridge);
// 通过桥使用目标
bridge.method();
// 实例化一个桥接目标
StandingBridge standingBridge = new StandingBridge();
// 把目标传入桥
bridge.setBridgeable(standingBridge);
// 通过桥统一的方法进去
bridge.method();
}
}
优势:
1. 实现了***抽象与实现部分的分离***。
- 提高了功能的*可扩展性。
- 提供了动态切换的方式。
弊端:
1. 增加了系统的***设计和理解难度***。
- 对于系统中的不同维度需要有明确的区分,因此受到局限。
代理模式
代理模式是为消费者提供一个代理对象,并由代理对象控制对实现对象的引用。
代理类实现一个功能接口,并且在构造时创建实现接口具体功能的类,消费者通过调用代理类的功能接口,由代理类去调用实际功能类。
它与装饰器模式非常类似,不同的是,类对象是在构造器中创建,而装饰器的类在外面构建传进来。
使用场景:需要限制访问权限时、需要区分处理某些方法时、需要扩展某些功能时。
优势:
- 中介隔离作用,可以将消费者与设计的功能对象分隔开,通过代理类进行调用。
- 符合开闭原则,不需要修改已经封装好的具体功能类。
弊端:
- 由于在客户端和真实对象之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。
- 实现代理模式需要额外的工作,有些代理模式的实现非常复杂。
/**
* 代理沟通的接口
*/
interface Proxyable {
/**
* 功能
*/
public void method();
}
/**
* 实现功能的具体类
*/
class ProxySource implements Proxyable {
@Override
public void method() {
System.out.println("作者出来说话");
}
}
-
静态代理
创建代理类,在符合开闭原则的情况下对目标对象进行功能扩展。
/** * 静态代理 */ class StaticProxy implements Proxyable { private ProxySource proxySource = null; StaticProxy() { proxySource = new ProxySource(); } @Override public void method() { System.out.println("代理人收到消息,去找作者"); proxySource.method(); System.out.println("代理人的任务做完了"); } }
优势:符合开闭原则,且对性能的影响极小。
弊端:维护时工作量大,需要为每一个新服务增加对应的代理。且当接口改变时,需要对代理类也进行修改。
-
动态代理
不需要手动创建代理类,只需要编写动态处理器。代理对象在程序运行时由反射机制进行动态生成。
/** * 动态代理 */ class DynamicProxy { /** * 配置动态代理调用 */ private static final InvocationHandler SOURCE_HANDLE = new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = method.invoke(new ProxySource(), args); System.out.println("动态代理人的任务做完了"); return result; } }; /** * 获取被代理的实例 */ Proxyable getProxy() { Proxyable proxyInstance; System.out.println("动态代理人开始到处找作者"); proxyInstance = (Proxyable) Proxy.newProxyInstance(ProxySource.class.getClassLoader(), new Class[] { Proxyable.class }, SOURCE_HANDLE); System.out.println("找到作者了"); return proxyInstance; } }
优势:
- 动态代理大大减少了开发的代码量。
- 减少了对业务接口的依赖,降低了耦合度。
弊端:
- 受限于仅支持interface的代理
- 因为采用反射机制,运行效率低下