设计模式(Design Patterns)
使用设计模式是为了可以重用代码,让代码更加容易被他人理解、保证代码的可靠性。设计模式使得代码编制真正工程化,是软件工程的基石。
在项目中合理的运用设计模式可以完美的解决很多问题,每个模式在现在中都有相应的原理来与之对应。
一、设计模式的分类
总体的来说设计模式有三种类型:
创建型模式:工厂方法模式,抽象工厂模式,单例模式,建造者模式,原型模式
结构型模式:适配器模式,装饰器模式,代理模式,外观模式,桥接模式,组合模式,享元模式
行为型模式:策略模式,模板方法模式,观察者模式,迭代子模式,责任链模式,命令模式,备忘录模式,状态模式,访问者模式,中介者模式,解释器模式
还有两个独立的模式:并发型模式,线程池模式。
二、设计模式的原则
1】开闭原则(open Close principle)
开闭原则就是说对扩展开放,对修改关闭。在程序需要进行扩展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以就是说:为了程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类。
2】里氏代换原则(Liskov Substitution Principle)
里氏代换原则是面向对象设计的基本原则之一。任何基类可以出现的地方,子类一定可以出现。LSP是继承复用的基石。只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开闭原则”的补充。实现“开闭原则”的关键是抽象化。而基类与子类的继承关系就是抽象化的具体实现。
3】依赖倒转原则(Dependence Inversion Principle)
依赖倒转原则是开闭原则的基础,是对接口编程,依赖于抽象而不依赖于具体。
4】接口隔离原则(Interface Segregation Principle)
使用多个隔离的接口,比使用单个接口更好。还是一个降低类之间的耦合度,从大型软件架构出发,为了升级和维护方便。降低依赖和耦合度。
5】迪米特原则(最少知道原则)(Demeter Principle)
一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。
6】合成复用原则(Composite Reuse Principle)
是尽量使用合成/聚合的方式,而不是使用继承。
三、各个模式的运用
1】工厂方法模式(Factory Method)
工厂方法模式为三种:
普通工厂模式,为建立一个工厂类,对实现了同一接口的一些类进行实例的创建。
package com.mjf.model.factory;
public interface Send {
public void send();
}
package com.mjf.model.factory;
/**
* 工厂实现类
* @author h
*
*/
public class MailSender implements Send {
@Override
public void send() {
// TODO Auto-generated method stub
System.out.println("this is mail send");
}
}
package com.mjf.model.factory;
/**
* 工厂实现类
* @author h
*
*/
public class SmsSender implements Send {
@Override
public void send() {
// TODO Auto-generated method stub
System.out.println("this is sms send");
}
}
package com.mjf.model.factory;
/**
* 工厂类
* @author h
*
*/
public class SendFactory {
public Send getSend(String type){
if("sms".equals(type)){
return new SmsSender();
}else if("mail".equals(type)){
return new MailSender();
}else{
System.out.println("请选择正确的类型");
return null;
}
}
}
/** 测试类 */
package com.mjf.test.factory;
import com.mjf.model.factory.Send;
import com.mjf.model.factory.SendFactory;
public class FactoryTest01 {
public static void main(String[] args) {
SendFactory send = new SendFactory();
Send s = send.getSend("mail");
s.send();
}
}
多个工厂方法模式,是对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能创建对象,而多个工厂方法模式是提供多个工厂方法,让他们分别创建对象。
package com.mjf.model.factory;
/**
* 工厂实现类
* @author h
*
*/
public class MailSender implements Send {
@Override
public void send() {
// TODO Auto-generated method stub
System.out.println("this is mail send");
}
}
package com.mjf.model.factory;
public interface Send {
public void send();
}
package com.mjf.model.factory;
/**
* 工厂实现类
* @author h
*
*/
public class SmsSender implements Send {
@Override
public void send() {
// TODO Auto-generated method stub
System.out.println("this is sms send");
}
}
package com.mjf.model.factory;
/**
* 工厂类(多个)
* @author h
*
*/
public class SendFactory2 {
public Send getSmsSend(){
return new SmsSender();
}
public Send getMailSend(){
return new MailSender();
}
}
/** 测试类 */
package com.mjf.test.factory;
import com.mjf.model.factory.Send;
import com.mjf.model.factory.SendFactory2;
public class FactoryTest02 {
public static void main(String[] args) {
SendFactory2 send2 = new SendFactory2();
//Send s = send2.getMailSend();
Send s = send2.getSmsSend();
s.send();
}
}
使用了多个工厂方法模式后,改进了可以在工厂实现类中添加方法来创建对象。
静态工厂方法模式,是将工厂方法模式里的方法设置为静态的,不需要创建实例可以直接调用。
package com.mjf.model.factory;
/**
* 工厂类(多个)
* @author h
*
*/
public class SendFactory3 {
public static Send getSmsSend(){
return new SmsSender();
}
public static Send getMailSend(){
return new MailSender();
}
}
/** 测试类 */
package com.mjf.test.factory;
import com.mjf.model.factory.Send;
import com.mjf.model.factory.SendFactory3;
public class FactoryTest03 {
public static void main(String[] args) {
//Send s = SendFactory3.getMailSend();
Send s = SendFactory3.getSmsSend();
s.send();
}
}
总体来说,工厂模式的作用范围为:出现了很多的对象需要进行创建时,并且用相同的接口。则可以通过工厂方法模式进行创建。
2】抽象工厂模式(Abstract Factory )
工厂方法模式有一个问题是,类的创建依赖于工厂类,也就是说,如果要扩展程序就要对工厂类进行修改,违背了开闭原则。
所以采用抽象工厂模式,创建多个工厂类,当增加新的功能,直接增加新的工厂类就行了,而不需要修改原来的代码。
package com.mjf.model.abstract_factory;
public interface Send {
public void send();
}
package com.mjf.model.abstract_factory;
/**
* 工厂实现类
* @author h
*
*/
public class MailSender implements Send {
@Override
public void send() {
// TODO Auto-generated method stub
System.out.println("this is mail send");
}
}
package com.mjf.model.abstract_factory;
/**
* 工厂实现类
* @author h
*
*/
public class SmsSender implements Send {
@Override
public void send() {
// TODO Auto-generated method stub
System.out.println("this is sms send");
}
}
package com.mjf.model.abstract_factory;
public class SmsFactory implements Provder {
@Override
public Send getProduct() {
// TODO Auto-generated method stub
return new SmsSender();
}
}
package com.mjf.model.abstract_factory;
public class MailFactory implements Provder {
@Override
public Send getProduct() {
// TODO Auto-generated method stub
return new MailSender();
}
}
package com.mjf.model.abstract_factory;
public interface Provder {
public Send getProduct();
}
/** 测试类 */
package com.mjf.test.abstract_factory;
import com.mjf.model.abstract_factory.MailFactory;
import com.mjf.model.abstract_factory.Provder;
import com.mjf.model.abstract_factory.Send;
//import com.mjf.model.abstract_factory.SmsFactory;
public class AbstactFactoryTest {
public static void main(String[] args) {
// Provder pro = new SmsFactory();
// Send product = pro.getProduct();
// product.send();
Provder pro = new MailFactory();
Send s = pro.getProduct();
s.send();
}
}
3】单例模式(singleton)抽象工厂模式的好处为:如果你现在想要增加一个功能,发及时信息,则只需要做一个实现类,实现send接口,做一个工厂类,实现provder接口,就可以了;无需去改动现成的代码。扩展性好。
单例对象是一种常用的设计模式。单例对象保证在jvm中,该对象只有一个实例存在。
单例的作用:1)某些类创建比较频繁,对于一些大型的对象,这是一笔很大的系统开销;2)省去了new操作符,降低了系统内存的使用频率,减轻GC的压力;3)有些类好像交易所的核心交易引擎控制交易的流程,如果该类可以创建多个的话,系统就会繁乱。
package com.mjf.model.singleton;
public class Singleton {
/** 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载 */
private static Singleton instance = null;
/** 创建私有化构造方法。防止被初始化 */
private Singleton(){}
public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
public Object getResolve(){
return instance;
}
}
其实单例模式使用的内部类来维护单例的实现,JVM内部的机制能够保证当一个类被加载的时,这个类的加载过程是线程互斥的。这个类使用是可以实现单例模式,但是存在着线程安全问题。但是,如果使用了synchronize锁定的话,性能会有所下降,当每次调用方法的时候,就会去为对象锁上锁。
package com.mjf.model.singleton;
public class Singleton02 {
/** 创建私有化构造方法。防止被初始化 */
private Singleton02(){}
/** 创建内部类来实现单例 */
private static class SingletonFactory {
private static Singleton02 instance = new Singleton02();
}
public static Singleton02 getInstance(){
return SingletonFactory.instance;
}
}
单例模式:
1、单例模式理解比较简单,但是实现有点难度。
2、Synchronized关键字锁定的是对象,在用的时候需要注意。
3、单例类比较灵活,毕竟从实现上只是一个普通的Java类,只要满足单例的基本需求,你可以在里面随心所欲的实现一些其它功能,但是静态类不行。从上面这些概括中,基本可以看出二者的区别,但是,从另一方面讲,我们上面最后实现的那个单例模式,内部就是用一个静态类来实现的,所以,二者有很大的关联,只是我们考虑问题的层面不同罢了。两种思想的结合,才能造就出完美的解决方案,就像HashMap采用数组+链表来实现一样,其实生活中很多事情都是这样,单用不同的方法来处理问题,总是有优点也有缺点,最完美的方法是,结合各个方法的优点,才能最好的解决问题!
4】建设者模式(Builder)
工厂模式提供的时创建单个类的模式,而建造者模式则是将各种产品集中起来进行管理,用来创建复合对象。所谓复合对象就是指某个类具有不同的属性;其实建造者模式就是工厂模式里面的对象创建多个进行管理。
package com.mjf.model.builder;
import java.util.ArrayList;
import java.util.List;
public class Builder {
private List<Send> list = new ArrayList<Send>();
public void produceMailSender(int count){
for(int i=0;i<count;i++){
list.add(new MailSender());
}
}
public void produceSmsSender(int count){
for(int i=0;i<count;i++){
list.add(new SmsSender());
}
}
}
package com.mjf.model.builder;
import java.util.ArrayList;
import java.util.List;
public class Builder {
private List<Send> list = new ArrayList<Send>();
public void produceMailSender(int count){
for(int i=0;i<count;i++){
list.add(new MailSender());
}
}
public void produceSmsSender(int count){
for(int i=0;i<count;i++){
list.add(new SmsSender());
}
}
}
package com.mjf.model.builder;
/**
* 工厂实现类
* @author h
*
*/
public class MailSender implements Send {
@Override
public void send() {
// TODO Auto-generated method stub
System.out.println("this is mail send");
}
}
package com.mjf.model.builder;
/**
* 工厂实现类
* @author h
*
*/
public class SmsSender implements Send {
@Override
public void send() {
// TODO Auto-generated method stub
System.out.println("this is sms send");
}
}
/** 测试类 */
package com.mjf.test.builder;
import com.mjf.model.builder.Builder;
public class BuilderTest {
public static void main(String[] args) {
Builder builder = new Builder();
builder.produceMailSender(10);
}
}
建造者模式将很多的功能集成到一个类里,这个类可以创造出比较复杂的东西。与工厂模式的区别:工厂模式关注的是创建单个产品,而建造者模式则关注创建符合对象。
5】原型模式(Prototype)
原型模式的思想是将一个对象作为原型,对其进行复制,克隆,产生一个和原对象类似的新对象。复制对象是通过clone()实现的,实现了Cloneable接口。
package com.mjf.model.prototype;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class Prototype02 implements Cloneable,Serializable {
private static final long serialVersionUID = 1L;
private String string;
private SerializableObject obj;
/** 浅复制 */
public Object clone() throws CloneNotSupportedException{
Prototype02 pro = (Prototype02) super.clone();
return pro;
}
/** 深复制 */
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();
}
/** 内部类 */
private class SerializableObject implements Serializable{
private static final long serialVersionUID = 1L;
}
/** setter and getter method */
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;
}
}
一个原型类,只需要实现Cloneable接口,复写clone方法,其实cloneable接口是个空接口,所以clone的方法名是随意取的。重点是super.clone()调用的是Object的clone()方法,Object的clone是native的。
浅复制:将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型,指向的还是原对象所指向的,复制的不彻底。
深复制:将一个对象复制后,不论基本数据类型还是引用类型,都是重新创建的。复制的完全更新。
要实现深复制,需要采用流的形式读入当前对象的二进制输入,再写出二进制数据对应的对象。