一、设计模式的分类
什么是设计模式:在某些场景下,针对某类问题的某种通用的解决方案
特点: 通用、可复用
设计模式有哪些
-
创建型模式 :对象实例化的模式,用于解耦对象的实例化过程。5种
工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式
-
结构型模式 :把类或对象结合在一起形成一个更大的结构。7种
适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式
-
行为型模式 : 类与对象交互,责任划分及算法。11种
策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式
共23种 - 额外两种:
并发型模式, 线程池模式
二、设计模式的六大原则
总原则:开闭原则(Open Close Principle)
对扩展开放,对修改关闭。使程序的扩展性好,易于维护和升级, 使用接口和抽象类等, 实现热插拔效果。
1、单一职责原则
每个类应该实现单一的职责,解耦。
2、里氏替换原则(Liskov Substitution Principle)
对“开-闭”原则的补充,对实现抽象化的具体步骤的规范。
子类对父类的方法尽量不要重写和重载。因为父类代表了定义好的结构,通过这个规范的接口与外界交互,子类不应该随便破坏它。
3、依赖倒转原则(Dependence Inversion Principle)
开闭原则的基础,面向接口编程,依赖于抽象而不依赖于具体。写代码时用到具体类时,不与具体类交互,而与具体类的上层接口交互。
4、接口隔离原则(Interface Segregation Principle)
每个接口中不存在子类用不到却必须实现的方法,如果不然,就要将接口拆分。使用多个隔离的接口,比使用单个接口(多个接口方法集合到一个的接口)要好。
5、迪米特法则(最少知道原则)(Demeter Principle)
一个类对自己依赖的类知道的越少越好。无论被依赖的类多么复杂,都应将逻辑封装在方法的内部,通过public方法提供给外部。这样当被依赖的类变化时,才能最小的影响该类。
6、合成复用原则(Composite Reuse Principle)
尽量首先使用聚合的方式,而不是使用继承
常见模式介绍
1) 单例模式
它核心结构只包含一个被称为单例类的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。
应用场景:如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。
懒汉式
class LazyMan{
private static LazyMan lm;
private LazyMan(){};
public static LazyMan getInstance(){
if (lm == null){
synchronized (LazyMan.class) {
if (lm == null) {
lm = new LazyMan();
}
}
}
return lm;
}
}
饿汉式
class HungeryMan{
private static HungeryMan hm;
private HungeryMan(){
hm = new HungeryMan();
};
public static HungeryMan getInstance(){
return hm;
}
}
2) 工厂方法模式
工厂模式主要是为创建对象提供了接口。
应用场景:
a、 在编码时不能预见需要创建哪种类的实例。
b、 系统不应依赖于产品类实例如何被创建、组合和表达的细节。
简单工厂模式不属于23种设计模式范畴。
简单工厂模式的问题:类的创建依赖工厂类,若想要拓展程序,必须对工厂类进行修改,这违背了闭包原则,所以,就有了工厂方法模式,创建一个工厂接口和创建多个工厂实现类,一旦需要增加新的功能,直接增加新的工厂类,不需要修改之前的代码。
一个顶级接口
public interface Sender {
public void Send();
}
两个实现类:
public class MailSender implements Sender {
@Override
public void Send() {
System.out.println("this is mailsender!");
}
}
public class SmsSender implements Sender {
@Override
public void Send() {
System.out.println("this is sms sender!");
}
}
两个工厂类:
public class SendMailFactory implements Provider {
@Override
public Sender produce(){
return new MailSender();
}
}
[java] view plaincopy
public class SendSmsFactory implements Provider{
@Override
public Sender produce() {
return new SmsSender();
}
}
再提供一个接口:
public interface Provider {
public Sender produce();
}
测试类:
public class Test {
public static void main(String[] args) {
Provider provider = new SendMailFactory();
Sender sender = provider.produce();
sender.Send();
}
}
3) 策略模式
定义了算法族,分别封装起来,让它们之间可以互相替换。此模式让算法的变化独立于使用算法的客户。
应用场景:
a、 一件事情,有很多方案可以实现。
b、我可以在任何时候,决定采用哪一种实现。
c.、未来可能增加更多的方案。
d、 策略模式让方案的变化不会影响到使用方案的客户。
策略模式的决定权在用户,系统本身提供不同算法的实现,新增或者删除算法,对各种算法做封装。因此,策略模式多用在算法决策系统中,外部用户只需要决定用哪个算法即可。
需要设计一个接口,为一系列实现类提供统一的方法,多个实现类实现该接口。可以设计一个抽象类(辅助类可有可无),提供辅助函数
首先统一接口:
public interface ICalculator {
public int calculate(String exp);
}
辅助类:
public abstract class AbstractCalculator {
public int[] split(String exp,String opt){
String array[] = exp.split(opt);
int arrayInt[] = new int[2];
arrayInt[0] = Integer.parseInt(array[0]);
arrayInt[1] = Integer.parseInt(array[1]);
return arrayInt;
}
}
三个实现类:
public class Plus extends AbstractCalculator implements ICalculator {
@Override
public int calculate(String exp) {
int arrayInt[] = split(exp,"\\+");
return arrayInt[0]+arrayInt[1];
}
}
public class Minus extends AbstractCalculator implements ICalculator {
@Override
public int calculate(String exp) {
int arrayInt[] = split(exp,"-");
return arrayInt[0]-arrayInt[1];
}
}
public class Multiply extends AbstractCalculator implements ICalculator {
@Override
public int calculate(String exp) {
int arrayInt[] = split(exp,"\\*");
return arrayInt[0]*arrayInt[1];
}
}
简单的测试类:
public class StrategyTest {
public static void main(String[] args) {
String exp = "2+8";
ICalculator cal = new Plus();
int result = cal.calculate(exp);
System.out.println(result);
}
}
输出:10
4) 观察者模式。
又被称作发布/订阅模式,定义了对象间一对多依赖,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
应用场景:
a、对一个对象状态的更新,需要其他对象同步更新,而且其他对象的数量动态可变。
b、对象仅需要将自己的更新通知给其他对象而不需要知道其他对象的细节。
一个Observer接口:
public interface Observer {
public void update();
}
两个实现类:
public class Observer1 implements Observer {
@Override
public void update() {
System.out.println("observer1 has received!");
}
}
public class Observer2 implements Observer {
@Override
public void update() {
System.out.println("observer2 has received!");
}
}
Subject接口及实现类:
public interface Subject {
/*增加观察者*/
public void add(Observer observer);
/*删除观察者*/
public void del(Observer observer);
/*通知所有的观察者*/
public void notifyObservers();
/*自身的操作*/
public void operation();
}
public abstract class AbstractSubject implements Subject {
private Vector<Observer> vector = new Vector<Observer>();
@Override
public void add(Observer observer) {
vector.add(observer);
}
@Override
public void del(Observer observer) {
vector.remove(observer);
}
@Override
public void notifyObservers() {
Enumeration<Observer> enumo = vector.elements();
while(enumo.hasMoreElements()){
enumo.nextElement().update();
}
}
}
public class MySubject extends AbstractSubject {
@Override
public void operation() {
System.out.println("update self!");
notifyObservers();
}
}
测试类:
public class ObserverTest {
public static void main(String[] args) {
Subject sub = new MySubject();
sub.add(new Observer1());
sub.add(new Observer2());
sub.operation();
}
}
输出:
update self!
observer1 has received!
observer2 has received!
5) 装饰器模式
给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例
应用场景:
a、需要扩展一个类的功能。
b、动态的为一个对象增加、撤销功能。(继承的功能是静态的,不能动态增删。)
缺点:产生过多相似的对象,不易排错
接口
public interface Sourceable {
public void method();
}
原始实现类
public class Source implements Sourceable {
@Override
public void method() {
System.out.println("the original method!");
}
}
代理类
public class Decorator implements Sourceable {
private Sourceable source;
public Decorator(Sourceable source){
super();
this.source = source;
}
@Override
public void method() {
System.out.println("before decorator!"); //装饰器
source.method();
System.out.println("after decorator!"); //装饰器
}
}
测试类:
public class DecoratorTest {
public static void main(String[] args) {
Sourceable source = new Source();
Sourceable obj = new Decorator(source);
obj.method();
}
}
输出:
before decorator!
the original method!
after decorator!
6) 代理模式
给一个对象提供一个代理,并由代理对象控制对原对象的引用。它使得客户不能直接与真正的目标对象通信。
代理对象是目标对象的代表,其他需要与这个目标对象打交道的操作都是和这个代理对象在交涉,起到中介的作用,既保护了目标对象的,同时也在一定程度上面减少了系统的耦合度。
应用场景:
如果已有的方法在使用的时候需要对原有的方法进行改进,此时有两种办法:
a、修改原有的方法来适应。这样违反了“对扩展开放,对修改关闭”的原则。
b、采用一个代理类调用原有的方法,对产生的结果进行控制。
使用代理模式,可以将功能划分的更加清晰,有助于后期维护
接口
public interface Sourceable {
public void method();
}
原始实现类
public class Source implements Sourceable {
@Override
public void method() {
System.out.println("the original method!");
}
}
代理类
public class Proxy implements Sourceable {
private Source source;
public Proxy(){
super();
this.source = new Source();
}
@Override
public void method() {
before();
source.method();
atfer();
}
private void atfer() {
System.out.println("after proxy!");
}
private void before() {
System.out.println("before proxy!");
}
}
测试类:
public class ProxyTest {
public static void main(String[] args) {
Sourceable source = new Proxy();
source.method();
}
}
输出:
before proxy!
the original method!
after proxy!
代理模式,偏重因自己无法完成或自己无需关心,需要他人干涉事件流程,更多的是对对象的控制。
装饰模式,偏重对原对象功能的扩展,扩展后的对象仍是是对象本身。
7) 迭代子模式。
提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示。
应用场景:
当你需要访问一个聚集对象,而且不管这些对象是什么都需要遍历的时候,就应该考虑用迭代器模式。其实stl容器就是很好的迭代器模式的例子。
两个接口
public interface Collection {
public Iterator iterator();
/*取得集合元素*/
public Object get(int i);
/*取得集合大小*/
public int size();
}
public interface Iterator {
//前移
public Object previous();
//后移
public Object next();
public boolean hasNext();
//取得第一个元素
public Object first();
}
两个实现:
public class MyCollection implements Collection {
public String string[] = {"A","B","C","D","E"};
@Override
public Iterator iterator() {
return new MyIterator(this);
}
@Override
public Object get(int i) {
return string[i];
}
@Override
public int size() {
return string.length;
}
}
public class MyIterator implements Iterator {
private Collection collection;
private int pos = -1;
public MyIterator(Collection collection){
this.collection = collection;
}
@Override
public Object previous() {
if(pos > 0){
pos--;
}
return collection.get(pos);
}
@Override
public Object next() {
if(pos<collection.size()-1){
pos++;
}
return collection.get(pos);
}
@Override
public boolean hasNext() {
if(pos<collection.size()-1){
return true;
}else{
return false;
}
}
@Override
public Object first() {
pos = 0;
return collection.get(pos);
}
}
测试类:
public class Test {
public static void main(String[] args) {
Collection collection = new MyCollection();
Iterator it = collection.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
}
输出:A B C D E
8) 模板方法模式
定义一个操作中的算法的骨架,将一些步骤延迟到子类中,模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些步骤。
应用场景:
对于一些功能,在不同的对象身上展示不同的作用,但是功能的框架是一样的。
public abstract class AbstractCalculator {
/*主方法,实现对本类其它方法的调用*/
public final int calculate(String exp,String opt){
int array[] = split(exp,opt);
return calculate(array[0],array[1]);
}
/*被子类重写的方法*/
abstract public int calculate(int num1,int num2);
public int[] split(String exp,String opt){
String array[] = exp.split(opt);
int arrayInt[] = new int[2];
arrayInt[0] = Integer.parseInt(array[0]);
arrayInt[1] = Integer.parseInt(array[1]);
return arrayInt;
}
}
public class Plus extends AbstractCalculator {
@Override
public int calculate(int num1,int num2) {
return num1 + num2;
}
}
测试类:
public class StrategyTest {
public static void main(String[] args) {
String exp = "8+8";
AbstractCalculator cal = new Plus();
int result = cal.calculate(exp, "\\+");
System.out.println(result);
}
}
输出:10