设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。一个具体的应用场景,选择对了合适的设计模式,会让代码开发效率更高,可维护性和阅读性更加优秀。
总体来说设计模式分3大类
1.创建型模式,共5种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式
2.结构型模式,共7种:适配器模式、装饰模式、代理模式、外观模式、桥接模式、组合模式、享元模式
3.行为型模式,共11种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式
掌握设计模式的6大原则,可以从这些原则中,结合具体设计模式,来深入了解该设计模式的优缺点,和应用场景的适用性(初学可以不必掌握)。
1.开闭原则
对扩展开放,对修改关闭
2.里氏代换原则
任何基类可以出现的地方,子类一定可以出现,只有当衍生类可以替换掉基类,软件功能不受到影响的时候,基类才能真正的被复用,而衍生类也能够在基类的基础上增加新的行为,这里是对开闭原则实现的补充。实现开闭原则的关键步骤是抽象化,而基类与衍生类的继承关系就是抽象化的具体实现,所以里氏替换原则是对实现抽象的具体步骤规范
3.依赖倒转原则
开闭原则的基础,针对接口编程,依赖于抽象而不依赖于具体
高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象
抽象不应该依赖于具体,具体应该依赖于抽象
4.接口隔离原则
使用多个隔离的接口,比使用单个接口要好
5.迪米特法则(最少知道原则)
一个实体应当尽量少的与其他实体之间发生相互作用
6.合成复用原则
尽量使用合成/聚合的方式,而不是继承
组合:黑箱复用,A类中的成员变量对象的内部实现细节,对于A 类是不可见的
继承:白箱复用
工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式
工厂模式是为了解耦:把对象的创建和使用的过程分开。就是Class A 想调用 Class B ,那么A只是调用B的方法,而至于 B的实例化,就交给工厂类。
a.普通工厂:建立一个工厂类,对实现了同一个接口的类进行实例化。核心思想就是工厂的一个方法produce通过不同的参数,返回不同的实例对象(这些实例对象都实现了sender接口,sender接口包含方法send()),而produce方法的返回值就是sender。这里运用了多态
b.多个工厂:建立一个工厂类,里面多个方法,都返回sender接口。在外部调用不同的方法,返回的都是sender接口,而sender接口调用send()方法,就会调用不同的实现。
c.静态工厂:将上面的多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可
******************工厂,负责创建需要发送消息的对象
public class Factory {
public Sender createSender(final String senderType) {
if ("Sms".equals(senderType)) {
return new Sms();
} else {
return new Email();
}
}
}
******************需要创建的相关对象
public interface Sender {
public void send();
}
//电子邮箱
public class Email implements Sender {
@Override
public void send() {
System.out.println("发送email");
}
}
//短信
public class Sms implements Sender {
@Override
public void send() {
System.out.println("发送sms");
}
}
******************测试类
public class Test {
public static void main(final String[] args) {
Factory factory = new Factory();
Sender sender = factory.createSender("Email");
sender.send();
}
}
结果:发送email
工厂模式可以降低代码重复。如果创建对象B的过程都很复杂,需要一定的代码量,而且很多地方都要用到,那么就会有很多的重复代码。我们可以这些创建对象B的代码放到工厂里统一管理。既减少了重复代码,也方便以后对B的创建过程的修改维护。(当然,我个人觉得也可以把这些创建过程的代码放到类的构造函数里,同样可以降低重复率,而且构造函数本身的作用也是初始化对象。不过,这样也会导致构造函数过于复杂,做的事太多,不符合java 的设计原则。)由于创建过程都由工厂统一管理,所以发生业务逻辑变化,不需要找到所有需要创建B的地方去逐个修正,只需要在工厂里修改即可,降低维护成本。同理,想把所有调用B的地方改成B的子类B1,只需要在对应生产B的工厂中或者工厂的方法中修改其生产的对象为B1即可,而不需要找到所有的new B()改为new B1()。
工厂方法模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了开闭原则。就用到抽象工厂模式,创建多个工厂类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码
抽象工厂
public interface AbstractFactory {
public Sender createSender();
}
//Email工厂负责创建email
public class EmailFactory implements AbstractFactory {
@Override
public Sender createSender() {
return new Email();
}
}
//sms工厂负责创建sms
public class SmsFactory implements AbstractFactory {
@Override
public Sender createSender() {
return new Sms();
}
}
public interface Sender {
public void send();
}
public class Email implements Sender {
@Override
public void send() {
System.out.println("发送email");
}
}
public class Sms implements Sender {
@Override
public void send() {
System.out.println("发送sms");
}
}
****************************************测试类
public class Test {
public static void main(final String[] args) {
AbstractFactory factory = new SmsFactory();
Sender sender = factory.createSender();
sender.send();
}
}
结果 发送sms
提供了一种创建对象的最佳方式。单例模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
public class SingleObject {
//创建 SingleObject 的一个对象
private static SingleObject instance = new SingleObject();
//让构造函数为 private,这样该类就不会被实例化
private SingleObject(){}
//获取唯一可用的对象
public static SingleObject getInstance(){
return instance;
}
public void showMessage(){
System.out.println("你好,二万!");
}
}
public class Test {
public static void main(String[] args) {
//SingleObject object = new SingleObject(); //会报错
//获取唯一可用的对象
SingleObject object = SingleObject.getInstance();
//显示消息
object.showMessage();
}
}
输出结果:你好,二万!
单例模式有以下几种实现方式
*****************************懒汉式,线程不安全,多线程中不能使用*************************
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
*****************************懒汉式,线程安全*************************
优点:第一次调用才初始化,避免内存浪费。
缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
*****************************饿汉式,线程安全*************************
优点:没有加锁,执行效率会提高。
缺点:类加载时就初始化,浪费内存,不是懒加载。
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
***************************** 双检锁/双重校验锁(DCL,即 double-checked locking)
这里是对懒汉式模式的优化,在调用方法的时候才调用,但是要根据对象是否为空来判断是否在方法中新建(这里就会有安全隐患,在两个线程同时判断为空的时候,会创建出两个对象,在这里要用锁synchronized,而多次判断锁又要消耗资源,所以还要在同步代码块外面增加一条判断对象是否为空,来优化同步锁)
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
建造者模式和工厂模式的区别就是,工厂模式创建的是单个产品,建造者模式则是将各种产品集中起来进行管理,用来创建复合对象,所谓复合对象就是指某个类具有不同的属性。将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。创建者模式隐藏了复杂对象的创建过程,它把复杂对象的创建过程加以抽象,通过子类继承或者重载的方式,动态的创建具有复合属性的对象。如果将抽象工厂模式看成汽车配件生产工厂,生产一个产品族的产品,那么建造者模式就是一个汽车组装工厂,通过对部件的组装可以返回一辆完整的汽车。主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
```
// 1.构造复杂对象默认实例
public class Mooncakes {
private int mSize;
private String mName;
public Mooncakes() {
super();
mSize = 10;
mName = "";
}
public void setmSize(int mSize) {
this.mSize = mSize;
}
public void setmName(String mName) {
this.mName = mName;
}
@Override
public String toString() {
return "Mooncakes [mSize=" + mSize + ", mName=" + mName + "]";
}
}
// 2.导向器,定义复杂对象的构建算法
public class Director {
private Builder mBuilder;
public Director(Builder builder) {
super();
mBuilder = builder;
}
// 对于月饼而言,步骤是不会变的,先定型后印花
public Mooncakes produceMooncakes() {
return mBuilder.setSize(4).setName("嫦娥").build();
}
}
// 3.抽象出对象的组装方式
public abstract class Builder {
protected Mooncakes mCakes = new Mooncakes();
public abstract Builder setSize(int size);
public abstract Builder setName(String name);
public Mooncakes build() {
return mCakes;
}
}
// 4.按照正常的方式设置
public class NormalBuilder extends Builder {
@Override
public Builder setSize(int size) {
if (size > 0) {
mCakes.setmSize(size);
}
return this;
}
@Override
public Builder setName(String name) {
if (name != null && !name.equals("")) {
mCakes.setmName(name);
}
return this;
}
}
// 5.按照我的方式设置
public class LanceBuilder extends Builder {
@Override
public Builder setSize(int size) {
if (size > 0) {
mCakes.setmSize(size * 2);
}
return this;
}
@Override
public Builder setName(String name) {
if (name != null && !name.equals("")) {
mCakes.setmName("欢乐" + name);
}
return this;
}
}
public class Company{
public static void main(String[] args) {
// 先制作模具,在生产月饼
Mooncakes normalCakes = new Director(new NormalBuilder()).produceMooncakes();
Mooncakes lanceCakes = new Director(new LanceBuilder()).produceMooncakes();
System.out.println(normalCakes.toString());
System.out.println(lanceCakes.toString());
}
}
以一个对象为模型,进行复制,克隆,来产生新的对象
比如说拷贝
浅复制:将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型,指向的还是原对象所指向的。
深复制:将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。简单来说,就是深复制进行了完全彻底的复制,而浅复制不彻底。
引用是什么?
引用是一种数据类型(保存在stack中),保存了对象在内存(heap,堆空间)中的地址,这种类型即不是我们平时所说的简单数据类型也不是类实例(对象);
public class Prototype implements Cloneable, Serializable {
private static final long serialVersionUID = 1L;
private String string;
private SerializableObject obj;
/* 浅复制 */
public Object clone() throws CloneNotSupportedException {
Prototype proto = (Prototype) super.clone();
return proto;
}
/* 深复制 */
public Object deepClone() throws IOException, ClassNotFoundException {
/* 写入当前对象的二进制流 */
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
/* 读出二进制流产生的新对象 */
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
}
public String getString() {
return string;
}
public void setString(String string) {
this.string = string;
}
public SerializableObject getObj() {
return obj;
}
public void setObj(SerializableObject obj) {
this.obj = obj;
}
}
class SerializableObject implements Serializable {
private static final long serialVersionUID = 1L;
//Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。、、serialVersionUID序列化和反序列化会失败
}
深复制还有一种方法,就是将需要复制的对象重写cloneable接口时,
单独为引用类型的成员变量A克隆
stu = (Student)super.clone();
class Address implements Cloneable{}
stu.addr = (Address)addr.clone(); //深度复制
这里的addr是类Address的一个实例,而且它的成员变量不能包含引用类型
适配器模式是将某个类接口转换成客户期望的另一个接口,目的是消除接口不兼容问题
类的适配器:
有一个source类,拥有一个方法,待适配,目标接口targetable,adapter类继承source类实现targetable接口
public class IPhoneCharger {
public void applePhoneCharge(){
System.out.println("The iPhone is charging ...");
}
}
package org.scott.adapter;
/**
* @author Scott
* @date 2013-11-23
* @description
*/
public interface ChargeAdapter {
public void phoneCharge();
}
package org.scott.adapterclass;
import org.scott.adapter.ChargeAdapter;
import org.scott.adapter.IPhoneCharger;
/**
* @author Scott
* @date 2013-11-23
* @description
*/
public class UniversalCharger extends IPhoneCharger implements ChargeAdapter{
@Override
public void phoneCharge() {
System.out.println("The phone is charging, but which kind of phone it is, who cares ...");
//iphone charging
super.applePhoneCharge();
}
}
package org.scott.adapterclass;
import org.scott.adapter.ChargeAdapter;
/**
* @author Scott
* @date 2013-11-23
* @description
*/
public class AdapterClassTest {
public static void main(String[] args) {
ChargeAdapter charger = new UniversalCharger();
charger.phoneCharge();
}
}
输出结果:The phone is charging, but which kind of phone it is, who cares ...
The iPhone is charging ...
对象的适配器
package org.scott.adapterobject;
import org.scott.adapter.ChargeAdapter;
import org.scott.adapter.IPhoneCharger;
/**
* @author Scott
* @date 2013-11-23
* @description
*/
public class UniversalCharger implements ChargeAdapter{
IPhoneCharger iphoneCharger;
public UniversalCharger(IPhoneCharger iphoneCharger){
this.iphoneCharger = iphoneCharger;
}
@Override
public void phoneCharge() {
System.out.println("The phone is charging, but which kind of phone it is, who cares ...");
iphoneCharger.applePhoneCharge();
}
}
package org.scott.adapterobject;
import org.scott.adapter.ChargeAdapter;
import org.scott.adapter.IPhoneCharger;
/**
* @author Scott
* @date 2013-11-23
* @description
*/
public class AdapterObjectTest {
public static void main(String[] args) {
IPhoneCharger iphoneCharger = new IPhoneCharger();
ChargeAdapter charger = new UniversalCharger(iphoneCharger);
charger.phoneCharge();
}
}
输出结果:The phone is charging, but which kind of phone it is, who cares ...
The iPhone is charging ...
接口的适配器:有时候,我们在接口中写了多个抽象方法,而接口的实现类就必须实现全部的方法,这里是比较不合时宜的,所以这里我们写一个中间的抽象类,让抽象类实现接口的抽象方法,实现类只跟抽象类打交道
类的适配器模式:当希望将一个类转换成满足另一个新接口的类时,可以使用类的适配器模式,创建一个新类,继承原有的类,实现新的接口即可。
对象的适配器模式:当希望将一个对象转换成满足另一个新接口的对象时,可以创建一个Wrapper类,持有原类的一个实例,在Wrapper类的方法中,调用实例的方法就行。
接口的适配器模式:当不希望实现一个接口中所有的方法时,可以创建一个抽象类Wrapper,实现所有方法,我们写别的类的时候,继承抽象类即可。
装饰模式就是,给对象动态的新增功能。这里是装饰对象和被装饰对象实现同一接口,然后装饰对象持有被装饰对象的实例。
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!");
}
}
装饰器模式的应用场景:
1、需要扩展一个类的功能。
2、动态的为一个对象增加功能,而且还能动态撤销。(继承不能做到这一点,继承的功能是静态的,不能动态增删。)
缺点:产生过多相似的对象,不易排错!
代理模式和装饰模式的实现代码几乎一样
二者最主要的区别是:代理模式中,代理类对被代理的对象有控制权,决定其执行或者不执行。
而装饰模式中,装饰类对代理对象没有控制权,只能为其增加一层装饰,以加强被装饰对象的功能,仅此而已
解密动态代理
外观模式,就是解决类与类之间的依赖关系,像spring一样,把类与类的关系配置到配置文件中,而外观模式就是将他们的关系放在一个facadel类中
降低了类与类之间的耦合度,这个模式没有涉及到接口
eg:就像电脑的启动与关闭,硬盘,cpu,内存,三者间的启动与关闭存在严重的依赖关系,如果不用电脑这个类来存放他们间的关系,那么者三者间必须要互相持有 对方的实例,这样是不可取的。用电脑这个类来处理三者间的关系,这三者依赖性就降低了。
public class CPU {
public void startup(){
System.out.println("cpu startup!");
}
public void shutdown(){
System.out.println("cpu shutdown!");
}
}
public class Memory {
public void startup(){
System.out.println("memory startup!");
}
public void shutdown(){
System.out.println("memory shutdown!");
}
}
public class Disk {
public void startup(){
System.out.println("disk startup!");
}
public void shutdown(){
System.out.println("disk shutdown!");
}
}
public class Computer {
private CPU cpu;
private Memory memory;
private Disk disk;
public Computer(){
cpu = new CPU();
memory = new Memory();
disk = new Disk();
}
public void startup(){
System.out.println("start the computer!");
cpu.startup();
memory.startup();
disk.startup();
System.out.println("start computer finished!");
}
public void shutdown(){
System.out.println("begin to close the computer!");
cpu.shutdown();
memory.shutdown();
disk.shutdown();
System.out.println("computer closed!");
}
}
User类如下:
public class User {
public static void main(String[] args) {
Computer computer = new Computer();
computer.startup();
computer.shutdown();
}
}
输出结果:
start the computer!
cpu startup!
memory startup!
disk startup!
start computer finished!
begin to close the computer!
cpu shutdown!
memory shutdown!
disk shutdown!
computer closed!
桥接模式就是把事物和具体实现分开,使他们可以各自独立的变化,一个接口有两个实现类,然后通过一个桥接类birdge持有这个接口,根据传入的实现类而调用不同的实现
public interface Sourceable {
public void method();
}
分别定义两个实现类:
public class SourceSub1 implements Sourceable {
@Override
public void method() {
System.out.println("this is the first sub!");
}
}
public class SourceSub2 implements Sourceable {
@Override
public void method() {
System.out.println("this is the second sub!");
}
}
定义一个桥,持有Sourceable的一个实例:
public abstract class Bridge {
private Sourceable source;
public void method(){
source.method();
}
public Sourceable getSource() {
return source;
}
public void setSource(Sourceable source) {
this.source = source;
}
}
public class MyBridge extends Bridge {
public void method(){
getSource().method();
}
}
测试类:
public class BridgeTest {
public static void main(String[] args) {
Bridge bridge = new MyBridge();
/*调用第一个对象*/
Sourceable source1 = new SourceSub1();
bridge.setSource(source1);
bridge.method();
/*调用第二个对象*/
Sourceable source2 = new SourceSub2();
bridge.setSource(source2);
bridge.method();
}
}
处理树型结构的问题时,比较方便,就是类的内部包含另外一个类对象(tree.root也是一个node对象)
Tree tree = new Tree("A");
TreeNode nodeB = new TreeNode("B");
TreeNode nodeC = new TreeNode("C");
nodeB.add(nodeC);
tree.root.add(nodeB);
import java.util.ArrayList;
import java.util.List;
public class Employee {
private String name;
private String dept;
private int salary;
private List subordinates;
//构造函数
public Employee(String name,String dept, int sal) {
this.name = name;
this.dept = dept;
this.salary = sal;
subordinates = new ArrayList();
}
public void add(Employee e) {
subordinates.add(e);
}
public void remove(Employee e) {
subordinates.remove(e);
}
public List getSubordinates(){
return subordinates;
}
public String toString(){
return ("Employee :[ Name : "+ name
+", dept : "+ dept + ", salary :"
+ salary+" ]");
}
}
public class CompositePatternDemo {
public static void main(String[] args) {
Employee CEO = new Employee("John","CEO", 30000);
Employee headSales = new Employee("Robert","Head Sales", 20000);
Employee headMarketing = new Employee("Michel","Head Marketing", 20000);
Employee clerk1 = new Employee("Laura","Marketing", 10000);
Employee clerk2 = new Employee("Bob","Marketing", 10000);
Employee salesExecutive1 = new Employee("Richard","Sales", 10000);
Employee salesExecutive2 = new Employee("Rob","Sales", 10000);
CEO.add(headSales);
CEO.add(headMarketing);
headSales.add(salesExecutive1);
headSales.add(salesExecutive2);
headMarketing.add(clerk1);
headMarketing.add(clerk2);
//打印该组织的所有员工
System.out.println(CEO);
for (Employee headEmployee : CEO.getSubordinates()) {
System.out.println(headEmployee);
for (Employee employee : headEmployee.getSubordinates()) {
System.out.println(employee);
}
}
}
}
输出结果:
Employee :[ Name : John, dept : CEO, salary :30000 ]
Employee :[ Name : Robert, dept : Head Sales, salary :20000 ]
Employee :[ Name : Richard, dept : Sales, salary :10000 ]
Employee :[ Name : Rob, dept : Sales, salary :10000 ]
Employee :[ Name : Michel, dept : Head Marketing, salary :20000 ]
Employee :[ Name : Laura, dept : Marketing, salary :10000 ]
Employee :[ Name : Bob, dept : Marketing, salary :10000 ]
主要目的是实现对象共享,即共享池,当系统中对象多的时候,可以减小开销,通常与工厂模式一起使用
数据库:,url、driverClassName、username、password及dbname,
这些属性对于每个连接来说都是一样的,所以就适合用享元模式来处理,建一个工厂类,将上述类似属性作为内部成员数据
然后构造函数初始化的时候,根据传入的连接池size,创建size个connection放在List
然后 每获取一次连接,pool就返回pool.get(0),然后pool.remove。
每次连接用完,返回的时候,pool.add(connection)
1、JAVA 中的 String,如果有则返回,如果没有则创建一个字符串保存在字符串缓存池里面。
2、数据库的连接池。
public class Car {
private String carColor;
public Car(final String carColor) {
this.carColor = carColor;
}
}
public class CarFactory {
private static final HashMap carMap = new HashMap<>();
public static Car getCar(final String carColor) {
Car car = carMap.get(carColor);
if (car == null) {
car = new Car(carColor);
carMap.put(carColor, car);
System.out.println("Creating " + carColor + " car");
}
return car;
}
}
public class Test {
public static void main(final String[] args) {
Car car = CarFactory.getCar("red");
Car car2 = CarFactory.getCar("red");
System.out.println(car == car2);
}
}
输出结果:
Creating red car
true
意图:允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。
主要解决:对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为。
何时使用:代码中包含大量与对象状态有关的条件语句。
如何解决:将各种具体的状态类抽象出来。
这些模式分四大类:父类与子类,类与类之间,类的状态,通过中间类
(1)策略模式
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 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
策略模式的决定权在用户,系统本身提供不同算法的实现,新增或者删除算法,对各种算法做封装。因此,策略模式多用在算法决策系统中, 外部用户只需要决定用哪个算法即可。
优点: 1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。
缺点: 1、策略类会增多。 2、所有策略类都需要对外暴露。
使用场景:
1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
2、一个系统需要动态地在几种算法中选择一种。
3、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
注意事项:
如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题
(2)模板方法模式
1、模板方法模式的优点
①模板方法模式通过把不变的行为搬移到父类,去除了子类中的重复代码。
②子类实现算法的某些细节,有助于算法的扩展。
③通过一个父类调用子类实现的操作,通过子类扩展增加新的行为,符合“开放-封闭原则”。
2、模板方法模式的缺点
按照设计习惯,抽象类负责声明最抽象、最一般的事物属性和方法,实现类负责完成具体的事务属性和方法,但是模板方式正好相反,子类执行的结果影响了父类的结果,会增加代码阅读的难度。
3、模板方法模式适合的场景
①多个子类有共有的方法,并且逻辑基本相同。
②重要、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能则由各个子类实现。
③重构时,模板方法是一个经常使用的方法,把相同的代码抽取到父类中,然后通过构造函数约束其行为。
一个抽象类中,有一个主方法,然后定义其他方法,其他方法可以是抽象也可以实际方法,定义一个类,继承该类,重写抽象方法, 通过调用抽象类中的方法,调用到子类的方法
package com.jxs.templateMethodExample;
/**
* Created by jiangxs on 2018/5/7.
*/
public class TestPaper {
public void testQuestion1() {
System.out.println("杨过得到,后来给了郭靖," +
"炼成倚天剑、屠龙刀的玄铁可能是[ ]\n" +
"a.球墨铸铁 b.马口铁 c.高速合金钢 d.碳素纤维");
System.out.println("答案: " + answer1());
}
public void testQuestion2() {
System.out.println("杨过、程英、陆无双铲除了情花,造成[ ]\n" +
"a.使这种植物不再伤人 b.使一种珍惜物种灭绝\n" +
"c.破坏了那个生物圈的生态平衡 d.造成该地区荒漠化");
System.out.println("答案: " + answer2());
}
public String answer1() {
return null;
}
public String answer2() {
return null;
}
public String answer3() {
return null;
}
}
public class TestPaperA extends TestPaper {
@Override
public String answer1() {
return "d";
}
@Override
public String answer2() {
return "c";
}
@Override
public String answer3() {
return "c";
}
}
public class TestPaperB extends TestPaper {
@Override
public String answer1() {
return "a";
}
@Override
public String answer2() {
return "a";
}
@Override
public String answer3() {
return "a";
}
}
public class TestPaperClient {
public static void main(String[] args) {
System.out.println("学生甲抄的试卷:");
TestPaperA studentA = new TestPaperA();
studentA.testQuestion1();
studentA.testQuestion2();
System.out.println();
System.out.println("学生乙抄的试卷:");
TestPaperB studentB = new TestPaperB();
studentB.testQuestion1();
studentB.testQuestion2();
}
}
学生甲做的试卷:
杨过得到,后来给了郭靖,炼成倚天剑、屠龙刀的玄铁可能是[ ]
a.球墨铸铁 b.马口铁 c.高速合金钢 d.碳素纤维
答案: d
杨过、程英、陆无双铲除了情花,造成[ ]
a.使这种植物不再伤人 b.使一种珍惜物种灭绝
c.破坏了那个生物圈的生态平衡 d.造成该地区荒漠化
答案: c
学生乙做的试卷:
杨过得到,后来给了郭靖,炼成倚天剑、屠龙刀的玄铁可能是[ ]
a.球墨铸铁 b.马口铁 c.高速合金钢 d.碳素纤维
答案: a
杨过、程英、陆无双铲除了情花,造成[ ]
a.使这种植物不再伤人 b.使一种珍惜物种灭绝
c.破坏了那个生物圈的生态平衡 d.造成该地区荒漠化
答案: a
(3)观察者模式
场景描述:以购票为核心业务(此模式不限于该业务),但围绕购票会产生不同的其他逻辑,如:
1、购票后记录文本日志
2、购票后记录数据库日志
3、购票后发送短信
4、购票送抵扣卷、兑换卷、积分
5、其他各类活动等
当一个对象发生变化时,跟他相关的对象也会发生变化,是一对多的关系
MySubject类就是我们的主对象,Observer1和Observer2是依赖于MySubject的对象,当MySubject变化时,
Observer1和Observer2必然变化。AbstractSubject类中定义着需要监控的对象列表,
可以对其进行修改:增加或删除被监控对象,且当MySubject变化时,负责通知在列表内存在的对象
一个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 vector = new Vector();
@Override
public void add(Observer observer) {
vector.add(observer);
}
@Override
public void del(Observer observer) {
vector.remove(observer);
}
@Override
public void notifyObservers() {
Enumeration 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!
(4)迭代子模式(分为白箱,黑箱)
自己写一些关于集合操作的类,这个类的核心是,在初始化的时候,将集合通过构造函数,赋值给自己内部成员集合变量
达到可以通过自己写的iterator得到上一个元素,第一个元素等等。
白箱聚集于外禀迭代子
public abstract class AbstractIterator {
/**
* 工厂方法,创建相应迭代子对象的接口
*
* @return
*/
public abstract Iterator Iterator();
}
public interface Iterator {
/**
* 迭代方法,返回当前元素
*/
public Object currentItem();
/**
* 迭代方法,移动至第一个元素
*/
public void first();
/**
* 迭代方法,是否为最后一个元素
*/
public boolean hasNext();
/**
* 迭代方法,移动至下一个元素
*/
public void next();
}
package Iterator;
/**
* @author Steven
* @version 创建时间:2019年6月20日 上午8:47:10
*
*/
public class IteratorImpl implements Iterator {
/**
* 持有被迭代的具体的聚合对象
*/
private List agg;
/**
* 内部索引,记录当前迭代到的索引位置
*/
private int index = 0;
/**
* 记录当前索引对象的大小
*/
private int size = 0;
/**
* 构造函数,传入具体的聚合对象,并获取聚合对象大小
*
* @param agg
*/
public IteratorImpl(final List agg) {
this.agg = agg;
size = agg.size();
index = 0;
}
/**
* 迭代方法,返还当前位置元素
*/
@Override
public Object currentItem() {
return agg.get(index);
}
/**
* 移动至第一个元素
*/
@Override
public void first() {
index = 0;
}
/**
* 迭代方法,是否是最后一个元素
*/
@Override
public boolean hasNext() {
return index >= size;
}
/**
* 迭代方法,移动到下一个元素
*/
@Override
public void next() {
if (index < size) {
index++;
}
}
}
package Iterator;
public class List extends AbstractIterator {
private T[] objArray = null;
/**
* 构造方法,传入聚合对象的具体内容
*
* @param
*
* @param objArray
*/
public List(final T[] objArray) {
this.objArray = objArray;
}
public void add() {
// TODO Auto-generated method stub
}
/**
* 取值方法,向外界提供聚集元素
*
* @param index
* @return
*/
public Object get(final int index) {
if (index < objArray.length) {
return objArray[index];
} else {
return null;
}
}
@Override
public Iterator Iterator() {
return new IteratorImpl(this);
}
/**
* 取值方法,向外界提供聚集的大小
*
* @return
*/
public int size() {
return objArray.length;
}
}
public class Test {
public static void main(final String[] args) {
final Integer[] arr = new Integer[3];
arr[0] = 1;
arr[1] = 2;
arr[2] = 3;
List list = new List<>(arr);
Iterator it = list.Iterator();
while (!it.hasNext()) {
System.out.println(it.currentItem());
it.next();
}
System.out.println(list.size());
System.out.println(list.get(0));
}
}
黑箱聚集于内禀迭代子
public abstract class AbstractIterator {
/**
* 工厂方法,创建相应迭代子对象的接口
*
* @return
*/
public abstract Iterator Iterator();
}
public interface Iterator {
/**
* 迭代方法,返回当前元素
*/
public Object currentItem();
/**
* 迭代方法,移动至第一个元素
*/
public void first();
/**
* 迭代方法,是否为最后一个元素
*/
public boolean hasNext();
/**
* 迭代方法,移动至下一个元素
*/
public void next();
}
public class List extends AbstractIterator {
/**
* 内部成员类,具体迭代子类
*
*
*
*/
private class IteratorImpl implements Iterator {
/**
* 内部索引,记录当前迭代到的索引位置
*/
private int index = 0;
/**
* 记录当前聚集对象的大小
*/
private int size = 0;
/**
* 构造函数,设置聚集对象大小和起始索引
*/
public IteratorImpl() {
size = objArray.length;
index = 0;
}
/**
* 迭代方法,返还当前位置元素
*/
@Override
public Object currentItem() {
return objArray[index];
}
/**
* 移动至第一个元素
*/
@Override
public void first() {
index = 0;
}
@Override
public boolean hasNext() {
return index >= size;
}
/**
* 迭代方法,移动到下一个元素
*/
@Override
public void next() {
if (index < size) {
index++;
}
}
}
private T[] objArray = null;
/**
* 构造方法,传入聚合对象的具体内容
*
* @param
*
* @param objArray
*/
public List(final T[] objArray) {
this.objArray = objArray;
}
@Override
public Iterator Iterator() {
return new IteratorImpl();
}
}
public class Test {
public static void main(final String[] args) {
final Integer[] arr = new Integer[3];
arr[0] = 1;
arr[1] = 2;
arr[2] = 3;
List list = new List<>(arr);
Iterator it = list.Iterator();
while (!it.hasNext()) {
System.out.println(it.currentItem());
it.next();
}
// System.out.println(list.size());没有这个方法
// System.out.println(list.get(0));
}
}
(5)责任链模式
有多个对象,每个对象持有下个对象的引用,这样就会形成一个链,直到某个对象处理了请求
此处强调一点就是,链接上的请求可以是一条链,可以是一个树,还可以是一个环,模式本身不约束这个,
需要我们自己去实现,同时,在一个时刻,命令只允许由一个对象传给另一个对象,而不允许传给多个对象。
String input = "1";
if ("1".equals(input)) {
//TODO do something
} else if ("2".equals(input)) {
//TODO do something
} else if ("3".equals(input)) {
//TODO do something
}
```
```
String input = "1";
switch (input) {
case "1":
//TODO do something
break;
case "2":
//TODO do something
break;
case "3":
//TODO do something
break;
default:
//TODO do something
break;
}
```
如果每个分支里面的逻辑比较简单,那还好,如果逻辑复杂,假设每个 case 大概要 100 行代码处理,有 10 个 case,一下子就出来一个「千行代码」文件。****而且还不利于维护、测试和扩展。
如果能够想办法把代码拆分成每个 case 一个文件,这样不仅代码逻辑清晰了很多,而且不管是后续维护、扩展还是进行测试,都方便很多。
```
public abstract class BaseCase {
// 为 true 表明自己可以处理该 case
private boolean isConsume;
public BaseCase(boolean isConsume) {
this.isConsume = isConsume;
}
// 下一个责任节点
private BaseCase nextCase;
public void setNextCase(BaseCase nextCase) {
this.nextCase = nextCase;
}
public void handleRequest() {
if (isConsume) {
// 如果当前节点可以处理,直接处理
doSomething();
} else {
// 如果当前节点不能处理,并且有下个节点,交由下个节点处理
if (null != nextCase) {
nextCase.handleRequest();
}
}
}
abstract protected void doSomething();
}
public class OneCase extends BaseCase {
public OneCase(boolean isConsume) {
super(isConsume);
}
@Override protected void doSomething() {
// TODO do something
System.out.println(getClass().getName());
}
}
String input = "1";
OneCase oneCase = new OneCase("1".equals(input));
TwoCase twoCase = new TwoCase("2".equals(input));
DefaultCase defaultCase = new DefaultCase(true);
oneCase.setNextCase(twoCase);
twoCase.setNextCase(defaultCase);
oneCase.handleRequest();
(6)命令模式,这里要区分命令模式和装饰模式(还有代理模式)
命令模式很好理解,举个例子,司令员下令让士兵去干件事情,从整个事情的角度来考虑,司令员的作用是,发出口令,口令经过传递,传到了士兵耳朵里,士兵去执行。
这个过程好在,三者相互解耦,任何一方都不用去依赖其他人,只需要做好自己的事儿就行
Receiver receiver = new Receiver();
Command cmd = new MyCommand(receiver);
Invoker invoker = new Invoker(cmd);
invoker.action();
举个例子,
让老师通知学习在工作日穿校服,在周末穿常服。
```
public abstract class AbstractCommand {
public abstract void execute();
}
public class DressOrdinaryClothes extends AbstractCommand {
private Student student;
/**
* 创建穿常服命令的时候,传入具体的学生对象,由学生对象操作自己
*
* @param light
*/
DressOrdinaryClothes(final Student student) {
this.student = student;
}
@Override
public void execute() {
student.dressOrdinaryClothes();
}
}
public class DressSchoolUniform extends AbstractCommand {
private Student student;
/**
* 创建穿校服命令的时候,传入具体的学生对象,由学生对象操作自己
*
* @param light
*/
DressSchoolUniform(final Student student) {
this.student = student;
}
@Override
public void execute() {
student.dressSchoolUniform();
}
}
public class Student {
public void dressOrdinaryClothes() {
System.out.println("今天周末,我们穿上常服了");
}
public void dressSchoolUniform() {
System.out.println("今天工作日,我们穿上校服了");
}
}
public class Teacher {
private AbstractCommand command;
/**
* 执行命令
*/
public void doCommand() {
command.execute();
}
/**
* 设置具体的命令
*
* @param command
*/
public void setCommand(final AbstractCommand command) {
this.command = command;
}
}
public class Test {
public static void main(final String[] args) {
Teacher teacher = new Teacher();
Student student = new Student();
// 学校通知老师今天周末,学生穿常服
DressOrdinaryClothes docCommand = new DressOrdinaryClothes(student);
teacher.setCommand(docCommand);
teacher.doCommand();
// 学校通知老师今天周末,学生穿校服
DressSchoolUniform dsuCommand = new DressSchoolUniform(student);
teacher.setCommand(dsuCommand);
teacher.doCommand();
}
}
输出结果:
今天周末,我们穿上常服了
今天工作日,我们穿上校服了
(1)备忘录模式(备份模式)
原始类a,b类存储a的一些属性,c类存储b(c只能存储不能修改)
备忘录模式的结构
public class Original {
private String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public Original(String value) {
this.value = value;
}
public Memento createMemento(){
return new Memento(value);
}
public void restoreMemento(Memento memento){
this.value = memento.getValue();
}
}
public class Memento {
private String value;
public Memento(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
public class Storage {
private Memento memento;
public Storage(Memento memento) {
this.memento = memento;
}
public Memento getMemento() {
return memento;
}
public void setMemento(Memento memento) {
this.memento = memento;
}
}
测试类:
[java] view plaincopy
public class Test {
public static void main(String[] args) {
// 创建原始类
Original origi = new Original("egg");
// 创建备忘录
Storage storage = new Storage(origi.createMemento());
// 修改原始类的状态
System.out.println("初始化状态为:" + origi.getValue());
origi.setValue("niu");
System.out.println("修改后的状态为:" + origi.getValue());
// 回复原始类的状态
origi.restoreMemento(storage.getMemento());
System.out.println("恢复后的状态为:" + origi.getValue());
}
}
输出:
初始化状态为:egg
修改后的状态为:niu
恢复后的状态为:egg
简单描述下:新建原始类时,value被初始化为egg,后经过修改,将value的值置为niu,最后倒数第二行进行恢复状态,
结果成功恢复了。其实我觉得这个模式叫“备份-恢复”模式最形象。
(2)状态模式
当状态改变时,同时改变其行为
public class State {
private String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public void method1(){
System.out.println("execute the first opt!");
}
public void method2(){
System.out.println("execute the second opt!");
}
}
[java] view plaincopy
package com.xtfggef.dp.state;
/**
* 状态模式的切换类 2012-12-1
* @author erqing
*
*/
public class Context {
private State state;
public Context(State state) {
this.state = state;
}
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
public void method() {
if (state.getValue().equals("state1")) {
state.method1();
} else if (state.getValue().equals("state2")) {
state.method2();
}
}
}
测试类:
[java] view plaincopy
public class Test {
public static void main(String[] args) {
State state = new State();
Context context = new Context(state);
//设置第一种状态
state.setValue("state1");
context.method();
//设置第二种状态
state.setValue("state2");
context.method();
}
}
(3)访问者模式
在访问者模式(Visitor Pattern)中,我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行为型模式。根据模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作。
```
//报销单查看者接口(相当于Visitor)
public interface AccountBookViewer {
// 查看报销的单子
void view(ConsumeBill bill);
}
//会计类,查看报销单的类之一
public class Accountant implements AccountBookViewer {
private double totalConsume;
public double getTotalConsume() {
System.out.println("会计报销费用,数目是:" + totalConsume);
return totalConsume;
}
@Override
public void view(final ConsumeBill bill) {
totalConsume += bill.getAmount();
}
}
//老板类,查看报销单的类之一
public class Boss implements AccountBookViewer {
List consumeContent = new ArrayList<>();
public void getConsumeContent() {
String content = "老板查看消费内容:" + consumeContent.stream().collect(Collectors.joining(","));
System.out.println(content);
}
// 注会在看账本时,如果是支出,则如果支出是工资,则需要看应该交的税交了没
@Override
public void view(final ConsumeBill bill) {
consumeContent.add(bill.getItem());
}
}
package visitor;
//消费的单子
public class ConsumeBill {
private double amount;
private String item;
public ConsumeBill(final double amount, final String item) {
this.amount = amount;
this.item = item;
}
public double getAmount() {
return amount;
}
public String getItem() {
return item;
}
public void show(final AccountBookViewer viewer) {
viewer.view(this);
}
}
public class Test {
public static void main(final String[] args) {
AccountBookViewer boss = new Boss();
AccountBookViewer ac = new Accountant();
ConsumeBill bill = new ConsumeBill(1000, "唱歌");
// 两个访问者分别访问账本
bill.show(ac);
bill.show(boss);
((Accountant) ac).getTotalConsume();
((Boss) boss).getConsumeContent();
}
}
输出结果:
会计报销费用,数目是:1000.0
老板查看消费内容:唱歌
(4)中介者模式
中介者模式也是用来降低类类之间的耦合的,因为如果类类之间有依赖关系的话,不利于功能的拓展和维护,因为只要修改一个对象,其它关联的对象都得进行修改
public interface Mediator {
public void createMediator();
public void workAll();
}
public class MyMediator implements Mediator {
private User user1;
private User user2;
public User getUser1() {
return user1;
}
public User getUser2() {
return user2;
}
@Override
public void createMediator() {
user1 = new User1(this);
user2 = new User2(this);
}
@Override
public void workAll() {
user1.work();
user2.work();
}
}
public abstract class User {
private Mediator mediator;
public Mediator getMediator(){
return mediator;
}
public User(Mediator mediator) {
this.mediator = mediator;
}
public abstract void work();
}
public class User1 extends User {
public User1(Mediator mediator){
super(mediator);
}
@Override
public void work() {
System.out.println("user1 exe!");
}
}
public class User2 extends User {
public User2(Mediator mediator){
super(mediator);
}
@Override
public void work() {
System.out.println("user2 exe!");
}
}
测试类:
[java] view plaincopy
public class Test {
public static void main(String[] args) {
Mediator mediator = new MyMediator();
mediator.createMediator();
mediator.workAll();
}
}
结果:
user1 exe!
user2 exe!