常用设计模式学习(一)
设计模式
这里是对几种常见设计模式进行学习。今天是面试回来复盘,面试官问的很基础,但是很遗憾的是,因为太基础没回答上。。。也是很无奈了。其实复习之后发现设计模式核心只为了三点,一是代码复用,二是解耦合,三是方便扩展,下面围绕这三点去学习几种常见的设计模式。
设计模式(Design Patterns),是一套可以被反复使用,多数人知晓的经过分类编目的,代码设计经验的总结。使用设计模式是为了可重用代码,方便他人理解,保证代码可靠性。
设计模式分三大类:
- 创建型模式,五种:工厂方法模式,抽象工厂模式,单例模式,建造者模式,原型模式。
- 结构型模式,七种:适配器模式,装饰器模式,代理模式,外观模式,桥接模式,组合模式,享元模式。
- 行为型模式,十一种:策略模式,模板方法模式,观察者模式,迭代子模式,责任链模式,命令模式,备忘录模式,状态模式,访问者模式,中介者模式,解释器模式。
- 还有其他两种,并发模式和线程池模式。
设计模式的六大原则
- 开闭原则(open close Principle):开闭原则就是对扩展开放,对修改关闭。在程序需要一个新功能的扩展和实现时候,不能去修改原有的代码,为了程序更好的扩展性,我们需要使用接口和抽象类。
- 里氏替换原则(Liskov Substitution Principle):面向对象设计的基本原则之一。里氏替换原则中说,任何基类可以出现的地方,其子类也一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。
- 依赖倒转原则(Dependence Inversion Principle):这个是开闭原则的基础,针对接口编程,依赖于抽象而不依赖于具体。
- 接口隔离原则(interface inversion Principle):使用多个隔离的接口,比使用单个接口要好。还是降低类之间的耦合度的意思。
- 迪米特法则(最少知道法则):一个实体应当尽量少的于其他实体之间发生相互作用,使得系统功能模块相互独立。
- 合成复用原则:原则是尽量使用合成的方式,而不是使用继承。
工厂模式
普通工厂模式
就是建立一个工厂类,对实现同一接口的一些类进行实例的创建。
/**
* 普通工厂设计模式Demo,角色接口Role
* @author 安澜
*/
public interface role {
public void method();
}
/**
* 普通工厂设计模式Demo,role的实现类student
* @author 安澜
*/
public class student implements role {
public void method() {
System.out.println("this is "+this.getClass().getSimpleName()+" of role");
}
}
/**
* 普通工厂设计模式Demo,role的实现类teacher
* @author 安澜
*/
public class teacher implements role {
public void method() {
System.out.println("this is "+this.getClass().getSimpleName()+" of role");
}
}
/**
* 普通工厂设计模式Demo,工厂类FactoryDesign
* @author 安澜
*/
public class FactoryDesign {
public role getRole(String type){
if("teacher".equals(type)){
return new teacher();
}else if("student".equals(type)){
return new student();
}else {
System.out.println("类型输入错误==》"+type);
}
return null;
}
}
/**
* 普通工厂设计模式Demo
* @author 安澜
*/
public class FactoryTest_One {
public static void main(String[] args) {
FactoryDesign factory = new FactoryDesign();
role teacher = factory.getRole("teacher");
teacher.method();
role student = factory.getRole("student");
student.method();
role aNull = factory.getRole("null");
aNull.method();
}
}
this is teacher of role
this is student of role
类型输入错误==》null
Exception in thread "main" java.lang.NullPointerException
at com.study.Design.demo.FactoryTest_One.main(FactoryTest_One.java:18)
多个工厂方法模式
对普通工厂模式的改进,在普通工厂模式中如果字符串错误则不能创建对象,而多个工厂模式提供多个工厂方法,分别创建对象。
/**
* 多个工厂方法模式Demo,工厂类MultipleDactoryMethod
* @author 安澜
*/
public class MultipleDactoryMethod {
/**
* get teacher
* @return role
*/
public role getTeacher(){
return new teacher();
}
/**
* get student
* @return role
*/
public role getStudent(){
return new student();
}
}
/**多个工厂方法模式Demo
* @author 安澜
*/
public class FactoryTest_Thre {
public static void main(String[] args) {
MultipleDactoryMethod multipleDactoryMethod = new MultipleDactoryMethod();
role student = multipleDactoryMethod.getStudent();
student.method();
role teacher = multipleDactoryMethod.getTeacher();
teacher.method();
}
}
this is student of role
this is teacher of role
静态工厂方法模式
就是将上面多个工厂模式中的方法设置为静态,不需要创建工厂的实例,直接调用即可。
/**
* 静态工厂方法模式Demo,工厂类StaticDactoryMethod
* @author 安澜
*/
public class StaticDactoryMethod {
/**
* get teacher
* @return role
*/
public static role getTeacher(){
return new teacher();
}
/**
* get student
* @return role
*/
public static role getStudent(){
return new student();
}
}
/**
* 静态工厂方法模式Demo
* @author 安澜
*/
public class FactoryTest_four {
public static void main(String[] args) {
role student = StaticDactoryMethod.getStudent();
role teacher = StaticDactoryMethod.getTeacher();
student.method();
teacher.method();
}
}
抽象工厂模式
工厂模式有一个缺陷,就是类的创建依赖于工厂类,也就是说如果我们需要拓展程序,必须对工厂类进行修改,违背了开闭原则,所以从设计模式的角度考虑,创建一个工厂类接口,让多个工厂类去实现这个接口,这样一旦需要增加新的功能,只需要增加新的工厂类就可以了,避免修改之前的代码。
/**
* 抽象工厂模式,工厂接口FactoryInterface
* @author 安澜
*/
public interface FactoryInterface {
/**
* get Role
* @return role
*/
public role getRole();
}
/**
* 抽象工厂模式,工厂接口的实现类StudentFactory
* @author 安澜
*/
public class StudentFactory implements FactoryInterface {
/**
* get Role
* @return role
*/
public role getRole() {
return new student();
}
}
/**
* 抽象工厂模式,工厂接口的实现类TeacherFactory
* @author 安澜
*/
public class TeacherFactory implements FactoryInterface {
/**
* get Role
* @return role
*/
public role getRole() {
return new teacher();
}
}
/**
* 抽象工厂模式,工厂接口的新增实现类PrincipalFactory
* @author 安澜
*/
public class PrincipalFactory implements FactoryInterface {
/**
* get Role
* @return role
*/
public role getRole() {
return new principal();
}
}
/**
* 抽象工厂模式Demo
* @author 安澜
*/
public class FactoryTest_Two {
public static void main(String[] args) {
FactoryInterface studentFactory = new StudentFactory();
FactoryInterface teacherFactory = new TeacherFactory();
PrincipalFactory principalFactory = new PrincipalFactory();
role principal = principalFactory.getRole();
principal.method();
role stu = studentFactory.getRole();
stu.method();
role tea = teacherFactory.getRole();
tea.method();
}
}
this is principal of role
this is student of role
this is teacher of role
建造者模式
工厂模式提供的是创建单个类的方法,而建造者模式则是将各种产品集中起来管理,用来创建复合对象,所谓复合对象就是指某个类具有不同的属性。
/**建造者模式Demo,建造者类BuilderDesign
* @author 安澜
*/
public class BuilderDesign {
List<role> roles=new ArrayList<role>();
/**
* teacher Builder
* @param count
*/
public void teacherBuilder(int count){
for(int i=1;i<=count;i++){
roles.add(new teacher());
}
}
/**
* student Builder
* @param count
*/
public void studentBuilder(int count){
for(int i=1;i<=count;i++){
roles.add(new student());
}
}
/**
* principal Builder
* @param count
*/
public void principalBuilder(int count){
for(int i=1;i<=count;i++){
roles.add(new principal());
}
}
}
/**
* 建造者模式Demo
* @author 安澜
*/
public class BuilderDemo {
public static void main(String[] args) {
BuilderDesign builderDesign = new BuilderDesign();
builderDesign.principalBuilder(1);
builderDesign.teacherBuilder(5);
builderDesign.studentBuilder(20);
}
}
单例模式
单例模式是一种常见的设计模式,在java应用中,单例对象能保证在一个jvm中,该对象只有一个实例存在。
- 避免某些大型对象频繁创建,节省系统开销。
- 省去new操作符的使用,降低系统内存的使用频率,减轻GC压力。
- 在一些特定的场景下保证核心服务独立控制整个流程。
单例模式的实现——私有构造方法,静态属性
/**这是一个简单的单例类,不能满足多线程环境下的单例模式
* @author 安澜
*/
public class SingletonClass {
/**
* 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载
*/
private static SingletonClass Instance= null;
/**
* 私有构造方法,防止被实例化
*/
private SingletonClass(){}
/**
* 静态工程方法,创建实例
*/
public static SingletonClass getInstance(){
return Instance=new SingletonClass();
}
/**
* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致
*/
public Object readResolve() {
return Instance;
}
}
单例模式的实现——Synchronized,双重检验
/**SingletonClass With Synchronized,满足多线程环境下的单例模式
* @author 安澜
*/
public class SingletonClassWithSynchronized {
/**
* 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载
*/
private static SingletonClassWithSynchronized Instance= null;
/**
* 私有构造方法,防止被实例化
*/
private SingletonClassWithSynchronized(){}
/**
* 静态工程方法,创建实例
* 这里使用双重检验防止多线程情景下创建了多个实例
*/
public static SingletonClassWithSynchronized getInstance(){
if(Instance==null) {
synchronized (Instance) {
if (Instance==null) {
return Instance=new SingletonClassWithSynchronized();
}
}
}
return Instance;
}
/**
* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致
*/
public Object readResolve() {
return Instance;
}
}
在Java指令中创建对象和赋值操作是分开进行的,也就是说Instance= new
SingletonClassWithSynchronized();语句是分两步执行的。但是JVM并不保证这两个操作的先后顺序,也就是说有可能JVM会为新的SingletonClassWithSynchronized实例分配空间,然后直接赋值给instance成员,然后再去初始化这个SingletonClassWithSynchronized实例。这样就可能出错了,我们以A、B两个线程为例:a>A、B线程同时进入了第一个if判断
b>A首先进入synchronized块,由于instance为null,所以它执行instance = new SingletonClassWithSynchronized();
c>由于JVM内部的优化机制,JVM先画出了一些分配给SingletonClassWithSynchronized实例的空白内存,并赋值给instance成员(注意此时JVM没有开始初始化这个实例),然后A离开了synchronized块。
d>B进入synchronized块,由于instance此时不是null,因此它马上离开了synchronized块并将结果返回给调用该方法的程序。
e>此时B线程打算使用SingletonClassWithSynchronized实例,却发现它没有被初始化,于是错误发生了。
单例模式的实现——静态内部类
/**SingletonClass With Factory,优化synchronized实现单例,满足多线程环境下的单例模式
* @author 安澜
*/
public class SingletonClassWithFactory {
/**
* 私有构造方法,防止被实例化
*/
private SingletonClassWithFactory(){}
/**
* 私有静态内部工厂类
*/
private static class SingletonFactory{
private static SingletonClassWithFactory instance=new SingletonClassWithFactory();
private static SingletonClassWithFactory getInstance(){
return SingletonFactory.instance;
}
}
}
JVM内部的机制能够保证当一个类被加载的时候,这个类的加载过程是线程互斥的。这样当我们第一次调用getInstance的时候,JVM能够帮我们保证instance只被创建一次,并且会保证把赋值给instance的内存初始化完毕,这样我们就不用担心上面的问题。
代理模式
代理模式就是多一个代理类出来,替元对象进行一些操作。
/**代理模式Demo,被代理接口ProxyInterface
* @author 安澜
*/
public interface ProxyInterface {
public void method();
}
/**代理模式Demo,元对象ProxyIMPL
* @author 安澜
*/
public class ProxyIMPL implements ProxyInterface {
/**
* 被代理方法method
*/
public void method() {
System.out.println("this is proxy method ");
}
}
/**代理模式Demo,代理类ProxyClass
* @author 安澜
*/
public class ProxyClass implements ProxyInterface {
private static ProxyIMPL proxyIMPL=new ProxyIMPL();
public void method() {
beforeMethod();
proxyIMPL.method();
afterMethod();
}
public void beforeMethod(){
System.out.println("this is before method ");
}
public void afterMethod(){
System.out.println("this is after method ");
}
public static void main(String[] args) {
ProxyClass proxyClass = new ProxyClass();
proxyClass.method();
}
this is before method
this is proxy method
this is after method
代理模式的应用场景:
如果已有的方法在使用的时候需要对原有的方法进行改进,此时有两种办法:
1、修改原有的方法来适应。这样违反了“对扩展开放,对修改关闭”的原则。
2、就是采用一个代理类调用原有的方法,且对产生的结果进行控制。这种方法就是代理模式。
使用代理模式,可以将功能划分的更加清晰,有助于后期维护!