常用设计模式
设计模式
1、软件设计七大原则
开闭原则
对扩展开放,对修改关闭。用抽象构建框架,用实现扩展细节
里氏代换原则
子类可以扩展父类的功能,但不能修改父类的功能;子类除了添加新方法外,不应修改父类的方法;子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
依赖倒转原则
高层模块不应该依赖底层模块,二者都应该依赖底层模块的抽象(接口);抽象不依赖于细节,细节应该依赖于抽象
controller -> service <- serviceimpl controller如果想调用业务层,直接调用的是service接口,而不是调用实现层,针对接口编程,不要针对实现编程
单一职责原则
一个类应该有且仅有一个引起它变化的原因,否则类应该被拆分。一个类/接口/方法只负责一项职责,针对的是不同角色
接口隔离原则
用多个专门的接口,而不使用单一的总接口,客户端不应该依赖它不需要的接口。针对的同一角色的不同功能,一个接口只服务于一个子模块或业务逻辑
迪米特法则
一个对象应该对其他对象保持最少的了解,使得系统功能模块相对独立,这样当一个模块修改时,影响的模块就会越少,扩展起来更加容易。只与你的直接朋友交谈,不跟“陌生人”说话。
朋友:
出现在成员变量、方法的输入、输出参数中的类称为成员朋友类,而出现在方法体内部的类不属于朋友类
合成复用原则
要求在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。
(用创建其他类的私有变量代替继承)
2、常用设计模式
单例模式
运行时只生成一个实例
饿汉式
package com.Gw.singleton;
public class Hungry {
/**
* 不论是否需要该对象,执行程序时都会生成实例
* 可能浪费空间
*/
private Hungry() {
}
private final static Hungry hungry= new Hungry();
public Hungry getInstance(){
return hungry;
}
}
懒汉式
package com.Gw.singleton;
public class LazyMan {
/**
* 可随用随建,但是在并发情况下不安全
*/
private LazyMan() {
}
private static LazyMan lazyMan;
public static LazyMan getInstance(){
if(lazyMan==null){
lazyMan = new LazyMan();
}
return lazyMan;
}
}
DCL式
package com.Gw.singleton;
public class LazyMan {
/**
* 双重检测锁模式,DCL懒汉式
*
*/
private LazyMan() {
}
private volatile static LazyMan lazyMan; //防止在new的过程中进行指令重排
public static LazyMan getInstance(){
if(lazyMan==null){
synchronized (LazyMan.class){
if(lazyMan==null){
lazyMan = new LazyMan();
/**
* new LazyMan()的过程仍不安全,不是原子性操作
* 1、分配内存空间
* 2、执行构造方法,初始化对象
* 3、把对象指向这个空间
* 但是2、3两步可能进行指令重排,此时如果有多个线程
* 当某一线程按照132执行13后,另一线程就会认为lazyMan不为null而返回,但此时lazyMan还未进行初始化
* 可以通过volatile防止指令重排
*
*/
}
}
}
return lazyMan;
}
}
静态内部类
package com.Gw.singleton;
public class StaticSingleton {
private StaticSingleton() {
}
private static class Internal{
private static final StaticSingleton INSTANCE = new StaticSingleton();
}
public StaticSingleton getInstance(){
return Internal.INSTANCE;
}
}
但是以上的单例模式都可以通过反射而被破坏,可以通过枚举防止单例模式被破坏
枚举实现单例模式
package com.Gw.singleton;
public enum enumTest {
INSTANCE;
public enumTest getInstance(){
return INSTANCE;
}
}
工厂模式
使用场景
1、日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。
2、数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。
3、设计一个连接服务器的框架,需要三个协议,"POP3"、"IMAP"、"HTTP",可以把这三个作为产品类,共同实现一个接口。
注意:对于简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式,增加复杂度。
简单工厂模式
通过接口调用对象,接口中放指定对象的反射类或者加个判断的String
/**
*
*通过在接口中指明具体的实现类,来达到创建指定对象的效果
*一个接口,多个实现类,用反射就很漂亮
*
*/
/**
* interface
*/
public interface Human {
public void getColor();
public void talk();
}
/**
* implements
*/
public class BlackHuman implements Human{
@Override
public void getColor() {
System.out.println("Black!!!");
}
@Override
public void talk() {
System.out.println("say black");
}
}
public class WhiteHuman implements Human{
@Override
public void getColor() {
System.out.println("White!!!");
}
@Override
public void talk() {
System.out.println("say white");
}
}
public class YellowHuman implements Human {
@Override
public void getColor() {
System.out.println("Yellow!!!");
}
@Override
public void talk() {
System.out.println("say yellow");
}
}
/**
* test
*/
public class TestFactory {
public static void main(String[] args) {
HumanFactory factory = new HumanFactory();
factory.createHuman(BlackHuman.class).getColor(); //反射指定具体的类
}
}
工厂方法模式
在简单工厂模式的基础上,为每个接口定义了一个工厂xxxFactory,这些工厂都继承了AbstractFactory。实际是通过这些xxxFactory创建对象,不再使用接口调用对象
/**
*
*此类建立在简单工厂模式的基础上
*此处所给的代码在简单工厂模式的代码上进行增加
*
*/
/**
* 抽象工厂类
*/
public abstract class AbstractHumanFactory {
public abstract Human createHuman();
}
/**
* 具体的工厂类,实际创建对象
*/
public class BlackHumanFactory extends AbstractHumanFactory {
@Override
public Human createHuman() {
return new BlackHuman();
}
}
public class WhiteHumanFactory extends AbstractHumanFactory {
@Override
public Human createHuman() {
return new WhiteHuman();
}
}
public class YellowHumanFactory extends AbstractHumanFactory {
@Override
public Human createHuman() {
return new YellowHuman();
}
}
/**
* test
*/
public class TestFactory {
public static void main(String[] args) {
AbstractHumanFactory factory = new BlackHumanFactory();
Human human = factory.createHuman(); //调用factory生成对象
human.getColor();
}
}
抽象工厂模式
在工厂方法模式下,产生了更多的组件,这些组件进行排列组合,因此可用一个factory产生一组对应场景的具体各个组件的类的组合,来解决对应的实际问题
如上图所示,抽象工厂模式主要由两大部分组成:AbstractFactory、xxxConcreteFactory;
其中AbstractFactory用来指明该工厂中有哪些组件,这些组件又对应着很多的实现类;
xxxConcreteFactory就是根据实际的问题对这些Component的各个实现类进行选择搭配。
在抽象工厂模式中,是通过xxxConcreteFactory来创建组件的对象的。
建造者模式
原理图
时序图
注意:建造者模式一定要关注时序问题,对于Builder类中定义的抽象方法,在ConcreteBuilder中进行了实现;
但是建造的顺序(即方法调用的顺序为指定),我们实际上使用的是Director类中construct方法来确定执行顺序
代码示例
Builder.java
/**
*
* 定义接口,交由具体的xxxBuilder去实现
*
*/
public abstract class Builder {
public abstract void makeString(String str);
public abstract void makeItems(String[] items);
public abstract void makeTitle(String title);
public abstract void close();
public abstract String getResult();
}
HtmlBuilder.java
/**
*
*实现html文本的建造
*
*/
public class HtmlBuilder extends Builder {
private String filename;
private PrintWriter writer;
@Override
public void makeString(String str) {
writer.println("<p>" + str + "</p>");
}
@Override
public void makeItems(String[] items) {
writer.println("<ul>");
for (String item : items) {
writer.println("<li>" + item + "</li>");
}
writer.println("</ul>");
}
@Override
public void makeTitle(String title) {
filename = title + ".html";
try {
writer = new PrintWriter(new FileWriter(filename));
} catch (IOException e) {
e.printStackTrace();
}
writer.println("<html> <head> <title>" + title + "</title> </head> <body>");
writer.println("<h1>" + title + "<h1>");
}
@Override
public void close() {
writer.println("</body></html>");
writer.close();
}
@Override
public String getResult() {
return filename;
}
}
TextBuilder.java
/**
*
* 实现text文本的建造
*
*/
public class TextBuilder extends Builder {
private StringBuffer buffer = new StringBuffer();
@Override
public void makeString(String str) {
buffer.append("=>" + str + "\n");
buffer.append("\n");
}
@Override
public void makeItems(String[] items) {
System.out.println("items:" );
for (String item : items) {
buffer.append(item + "+");
}
buffer.append("\n");
}
@Override
public void makeTitle(String title) {
buffer.append("================begin==============" + "\n");
buffer.append("[" + title + "]" + "\n");
buffer.append("\n");
}
@Override
public void close() {
buffer.append("================end=================" + "\n");
buffer.append("\n");
}
@Override
public String getResult() {
return buffer.toString();
}
}
Director.java
/**
* 建造者模式的关键所在!!!!
* 指出了建造者模式的特点
*/
public class Director {
private Builder builder;
public Director(Builder builder) {
this.builder = builder;
}
/**
*
* 关键步骤!!!!
* 有了原材料之后(即对Builder有了不同的实现之后),确定各个方法间如何协调执行
*
*/
public void construct(){
builder.makeTitle("Greeting");
builder.makeItems(new String[]{
"morning",
"afternoon",
"night"
});
builder.makeString("nice!!!");
builder.makeItems(new String[]{
"good",
"good",
"good"
});
builder.close();
System.out.println(builder.getResult());
}
}
Client.java
/**
* 建造者模式的使用
*/
public class Client {
public static void main(String[] args) {
TextBuilder textBuilder = new TextBuilder();
HtmlBuilder htmlBuilder = new HtmlBuilder();
Director director = new Director(htmlBuilder);
director.construct();
}
}
小结
模板方法模式
原理图
在模板方法模式中,需要在抽象模板中定义好抽象的需要公共实现的接口方法和非抽象的调用模板方法的算法实现
和建造者模式的区别
- 使用组合方式实现不同的表示,而模板方法使用的是继承的方式,组合优于继承
- 在模板模式中将建造者模式的Director和Builder合并起来放在了抽象的模板中
- 建造者模式中的Director可以有多种实现方式;而模板方法模式的方法调用固定,仅接口可修改
- 建造者模式延迟到子类的是各部件的创建,封装的是最后的构建流程。而模板方法模式延迟到子类实现的是算法的某些步骤,封装的是算法骨架
- 建造者模式中,各部件的建造需要客户端配合完成,因此,建造各部件的方法需要是public的,而模板方法模式中,各单独的算法步骤不应该被客户端直接调用,因此通常是protected的。
适配器模式
先举几个例子,读卡器是作为内存卡和笔记本之间的适配器、jdbc也是一种适配器。
在java程序中,将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
注意事项:适配器不是在详细设计时添加的,而是解决正在服役的项目的问题。
类适配器模式
使用继承的适配器
Banner.java
/**
* 目标的输出
*/
public class Banner {
private String str;
public Banner(String str) {
this.str = str;
}
public void targetWeak(){
System.out.println("(" + str + ")");
}
public void targetStrong(){
System.out.println("**" + str + "**");
}
}
Print.java
/**
*
* 拓展后的Print接口需要目标的输出
*
*/
public interface Print {
public void printWeak();
public void printStrong();
public void otherThings();
}
PrintBanner.java
/**
*
* adapter
* 适配器的具体实现:使用接口和继承
*
*/
public class PrintBanner extends Banner implements Print {
public PrintBanner(String str) {
super(str);
}
@Override
public void printWeak() {
targetWeak(); //调用继承的Banner中的方法,来实现Print接口和Banner类适配
}
@Override
public void printStrong() {
targetStrong();
}
@Override
public void otherThings() {
System.out.println("the implement could do other things"); //除了适配的部分,还可以做其他的事情
}
}
test.java
/**
* test
*/
public class test {
public static void main(String[] args) {
PrintBanner test = new PrintBanner("test");
test.printWeak();
test.printStrong();
test.otherThings();
}
}
对象适配器模式
使用委托的适配器
与继承的只有两部分代码不同
Print.java
/**
* 使用抽象类代替接口
*/
public abstract class Print {
public abstract void printWeak();
public abstract void printStrong();
public abstract void otherThings();
}
PrintBanner.java
/**
*
* adapter
* 与继承实现不同,委托实现则是直接将Banner对象私有
* 适配器的具体实现:使用接口和继承
*
*/
public class PrintBanner extends Print { //继承代替接口,增加二者的耦合度
private Banner banner; //委托代替继承
public PrintBanner(String str) {
this.banner = new Banner(str);
}
@Override
public void printWeak() {
banner.targetWeak();
}
@Override
public void printStrong() {
banner.targetStrong();
}
@Override
public void otherThings() {
System.out.println("the implement could do other things"); //除了适配的部分,还可以做其他的事情
}
}
代理模式
责任链模式
由于很多问题不能一开始就判断某个方法能否解决,必须执行一遍才能判断是否解决问题,可以通过责任链模式;
如果不使用责任链直接用if硬编码在程序中,则在增加处理类的时候还需要更改原有主程序的代码,使用责任链可以更好的进行拓展
如果使用if-else
- 类比较庞大,各个方法都集中在该类中,违反了 "单一职责原则",测试和维护难度大
- 当需要修改该判断的顺序时,必须修改该类源代码(并重新进行严格地测试),违反了 "开闭原则"
- 该流程缺乏灵活性,流程确定后不可再修改(除非修改源代码),客户端无法定制流程
好处
- 弱化了发出请求的人和处理请求的人的关系
- 可以动态的改变职责链
- 使每个处理类专注自己的工作
原理
示例代码
Trouble.java
package com.gw.ChainOfResponsibility;
/**
* 设置具体的问题
* 以及问题的具体信息,xxxSupport类拿到后判断能不能进行处理
*/
public class Trouble {
private int number;
public Trouble(int number) {
this.number = number;
}
public int getNumber() {
return number;
}
@Override
public String toString() {
return "Trouble{" +
"number=" + number +
'}';
}
}
Support.java
package com.gw.ChainOfResponsibility;
public abstract class Support {
private String name;
private Support next;
public Support(String name) {
this.name = name;
}
public Support setNext(Support next) {
this.next = next;
return next;
}
/**
* 关键代码
* support方法:能解决就解决,不能解决看后面还有没有人可以传
*/
public final void support(Trouble trouble){
if(resolve(trouble)){
done(trouble);
}else if(next != null){
next.support(trouble);
}else{
fail(trouble);
}
}
protected abstract boolean resolve(Trouble trouble);
protected void done(Trouble trouble){
System.out.println(trouble.toString() + " is resolved by " + this.name);
}
protected void fail(Trouble trouble){
System.out.println("can't resolve " + trouble.toString());
}
}
NoSupport.java
package com.gw.ChainOfResponsibility;
public class NoSupport extends Support{
public NoSupport(String name) {
super(name);
}
@Override
protected boolean resolve(Trouble trouble) {
return false;
}
}
LimitSupport.java
package com.gw.ChainOfResponsibility;
public class LimitSupport extends Support{
private int limit;
public LimitSupport(String name, int limit) {
super(name);
this.limit = limit;
}
@Override
protected boolean resolve(Trouble trouble) {
if(trouble.getNumber() < this.limit){
return true;
}else{
return false;
}
}
}
OddSupport.java
package com.gw.ChainOfResponsibility;
public class OddSupport extends Support {
public OddSupport(String name) {
super(name);
}
@Override
protected boolean resolve(Trouble trouble) {
if(trouble.getNumber() % 2 != 0) return true;
return false;
}
}
SpecialSupport.java
package com.gw.ChainOfResponsibility;
public class SpecialSupport extends Support{
private int number;
public SpecialSupport(String name, int number) {
super(name);
this.number = number;
}
@Override
protected boolean resolve(Trouble trouble) {
if(trouble.getNumber() == this.number) return true;
return false;
}
}
迭代器模式
Aggregate.java
package com.gw.Iterator;
public interface Aggregate {
public abstract Iterator iterator();
}
Iterator.java
package com.gw.Iterator;
public interface Iterator {
public abstract boolean hasNext();
public abstract Object next();
}
Book.java
package com.gw.Iterator;
public class Book {
private String name;
public Book(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
BookShelf.java
package com.gw.Iterator;
/**
* BookShelf通过实现Aggregate接口,这个接口中只有一个方法,获取遍历器Iterator
*/
public class BookShelf implements Aggregate {
private Book[] books;
private int last = 0;
public BookShelf(int maxsize) {
books = new Book[maxsize];
}
public Book getIndex(int index){
return books[index];
}
public void appendBook(Book book){
this.books[last] = book;
last++;
}
public int getLength(){
return last;
}
/**
* 关键步骤
* @return
*/
@Override
public Iterator iterator() {
return new BookShelfIterator(this);
}
}
BookShelfIterator.java
package com.gw.Iterator;
public class BookShelfIterator implements Iterator{
private BookShelf bookShelf;
private int index;
public BookShelfIterator(BookShelf bookShelf) {
this.bookShelf = bookShelf;
this.index = 0;
}
/**
* 实现迭代器的逻辑,hasNext和next
* @return
*/
@Override
public boolean hasNext() {
if(index < bookShelf.getLength()) return true;
return false;
}
@Override
public Object next() {
Book book = bookShelf.getIndex(index);
index++;
return book;
}
}
装饰器模式
动态的给对象增加职责
代码示例
Info.java
package com.gw.Decorator;
/**
* 定义装饰物与被装饰者的公共接口
*/
public abstract class Info {
public abstract String getBasicInfo();
public abstract String getAllInfo();
}
Person.java
package com.gw.Decorator;
public class Person extends Info {
private String name;
public Person(String name) {
this.name = name;
}
/**
* 配置人物的基本信息,原始信息就是名字
* @return
*/
@Override
public String getBasicInfo() {
return name;
}
@Override
public String getAllInfo() {
return name;
}
}
AdditionalInfo.java
package com.gw.Decorator;
public abstract class AdditionalInfo extends Info{
protected Info info;
/**
* 注意再添加附件时需要传入一个Info对象(也就是person对象)
* 添加附件必须有一个明确的被装饰者指向
* @param info
*/
public AdditionalInfo(Info info) {
this.info = info;
}
/**
* 添加装饰物的公共接口
* @return
*/
public abstract String getAdditionalInfo();
}
Arms.java
package com.gw.Decorator;
/**
*装备的武器,是对AdditionalInfo的具体实现
*/
public class Arms extends AdditionalInfo{
private String ArmName;
public Arms(Info info, String name) {
super(info);
this.ArmName = name;
}
/**
* 获取武器的信息
* @return
*/
@Override
public String getAdditionalInfo() {
return ArmName;
}
/**
* 对被装饰者进行处理
* @return
*/
@Override
public String getBasicInfo() {
return info.getAllInfo(); //获取父类的所有信息,包括被装饰的信息
}
@Override
public String getAllInfo() {
return getBasicInfo() + " " + getAdditionalInfo(); //将装饰的信息添加进被装饰者的信息中
}
}
Mount.java
package com.gw.Decorator;
/**
*原理与Arms类似,具体参考Arms中的注释
*/
public class Mount extends AdditionalInfo {
private String MountName;
public Mount(Info info, String MountName) {
super(info);
this.MountName = MountName;
}
@Override
public String getAdditionalInfo() {
return MountName;
}
@Override
public String getBasicInfo() {
return info.getAllInfo();
}
@Override
public String getAllInfo() {
return getBasicInfo() + " " + getAdditionalInfo();
}
}
test.java
package com.gw.Decorator;
/**
* 创建一个person,再为这个person对象添加配件,进行装饰
*
* 创建关羽,为关羽添加坐骑和武器
*
*/
public class test {
public static void main(String[] args) {
Info person = new Person("关羽");
Info arm = new Arms(person, "青龙偃月刀");
Info mount = new Mount(arm, "赤兔马");
System.out.println(mount.getAllInfo());
}
}
策略模式
将对于某种问题的算法解决专门定义一个接口,方便以后进行算法的优化或者算法的拓展
观察者模式
适用于根据对象状态进行相应处理的场景
代码示例
Observer.java
package com.gw.Observer;
public interface Observer {
public abstract void update(NumberGenerator numberGenerator);
}
DigitObserver.java
/**
* 具体的观察者,观察到了目标状态后应如何执行update
*/
public class DigitObserver implements Observer {
/**
* 可以获取被观察的对象的信息,在update中进行使用
* @param numberGenerator
*/
@Override
public void update(NumberGenerator numberGenerator) {
System.out.println(111111);
}
}
GraphObserver.java
package com.gw.Observer;
public class GraphObserver implements Observer{
@Override
public void update(NumberGenerator numberGenerator) {
System.out.println("********");
}
}
NumberGenerator.java
package com.gw.Observer;
import java.util.ArrayList;
/**
* Subject主题
*/
public abstract class NumberGenerator {
private ArrayList<Observer> observers = new ArrayList<>();
public void addObserver(Observer observer){
observers.add(observer);
}
public void deleteObserver(Observer observer){
observers.remove(observer);
}
public void notifyObservers(){
for (Observer observer : observers) {
observer.update(this);
}
}
public abstract int getNumber();
public abstract void execute();
}
RandomNumberGenerator.java
package com.gw.Observer;
import java.util.Random;
public class RandomNumberGenerator extends NumberGenerator{
private Random random = new Random();
private int number;
@Override
public int getNumber() {
return number;
}
/**
* 根据当前状态决定是否向observer发送通知
*/
@Override
public void execute() {
for (int i = 0; i < 20; i++) {
number = random.nextInt(50);
notifyObservers();
}
}
}
test.java
package com.gw.Observer;
public class test {
public static void main(String[] args) {
Observer digitObserver = new DigitObserver();
Observer graphObserver = new GraphObserver();
NumberGenerator numberGenerator = new RandomNumberGenerator();
numberGenerator.addObserver(digitObserver);
numberGenerator.addObserver(graphObserver);
numberGenerator.execute();
}
}