最近学习基本的设计模式,在这里整理下,做个笔记。
设计模式其实就是一种解决问题的方法,Java作为一种面向对象语言,对所有的设计模式总少不了对接口的实现和对抽象类的继承。
适配器模式:一个适配类允许通常因为接口不兼容而不能一起工作的类工作在一起,它的特点就是兼容,从代码上来说就是适配类与原有类具有相同的接口,并且持有新的目标对象。
现在有一个老接口,老接口不能满足需求了,而老接口不能修改,那么就要让新接口和老接口进行适配。
```
/**
* 原接口,要传入参数Id name
* @author zhaoxu
*
*/
public interface OldApi {
public void update(int Id,String name);
}
实现类
public class OldApiiMPL implements OldApi {
@Override
public void update(int Id,String name){
System.out.println(name+"的Id是"+Id);
}
}
测试类
public class Test {
public static void main(String[] args) {
OldApi oldapi = new OldApiImpl();
oldapi.update(2, "李四");
}
}
运行结果是:
现在对于新的接口,只需要传入Id就行
定义一个新的接口:
public interface NewApi {
//只需要传入Id就行
public void update(int Id);
}
新的实现类:
public class NewApiImpl implements NewApi {
//对象适配,在这里先拿到原接口实现类的对象
OldApi oldapi;
public NewApiImpl(){
oldapi = new OldApiImpl();
}
@Override
public void update(int Id){
//这里适配的方式随意,只要兼容原有的,这里只是简单的实现
oldapi.update(Id, "李四");
}
}
测试类:
public class Test {
public static void main(String[] args) {
NewApi newapi = new NewApiImpl();
newapi.update(2);
}
}
运行结果:
这一套例子里面新的类持有了老接口的对象,在新方法里面进行适配,兼容了老接口和新接口。
代理模式:隔离调用类和被调用类,通过一个代理类来进行调用。
还是刚才那个例子,我现在要对原有的接口改造,实现只能让特有的人直接调用,比如只有name为USER的人可以直接调用,其他人的输入Id和name。
代理类:
public class ProxyApi implements OldApi{
//在这里先拿到原来接口实现类的对象
OldApi oldapi;
public ProxyApi(){
oldapi =new OldApiImpl();
}
@Override
public void update(int Id,String name){
if("USER".equals(name)){
oldapi.update(Id, name);
}else{
System.out.println("你的name不是USER,请输入Id和name");
}
}
}
测试类:
public class Test {
public static void main(String[] args) {
OldApi proxyapi = new ProxyApi();
proxyapi.update(2,"李四");
proxyapi.update(2, "USER");
}
运行结果:
这里的代理类必须要实现原接口,并且持有原接口的对象,才能称为代理类。这样就可以用代理类来过滤。
有的人说那为什么不对原有的实现类直接修改呢,那是因为Java的设计模式的基本原则开闭原则,即对外扩展开放,对内修改关闭。
装饰设计模式:原有的不能满足现有的要求,对原有的进行增强。
就是说丰富原有接口的功能。在这里被装饰类的对象必须作为参数传入装饰类里面。
还是上面的例子,在原有的接口功能里面加上显示当前的时间这个功能。
public class NewDateApiImpl implements OldApi{
//被装饰类的对象当做参数传入,这里就是和代理模式区别的地方,
//代理模式一定是自身持有这个对象
OldApi oldapi;
public NewDateApiImpl(OldApi oldapi){
this.oldapi = oldapi;
}
@Override
public void update(int Id,String name){
oldapi.update(Id, name);
System.out.println("当前的时间为"+new Date());
}
}
测试类:
public class Test {
public static void main(String[] args) {
OldApi oldapi = new NewDateApiImpl(new OldApiImpl());
oldapi.update(2,"李四");
}
}
运行结果为:
这里面被装饰类是原接口OldApi,装饰类就是NewDateApiImpl,被装饰类作为参数传入到装饰类中。这就是和代理模式不同的地方,代理模式一定是自身持有这个对象,而不是从外部传入。
工厂设计模式:客户端不再需要负责对象的创建,而由工厂类来创建,明确了各个类的职责。
抽象接口类:
public class Animal{
public void eat(){}
}
具体实现类:
public class Cat extends Animal{
public void eat(){
System.out.println("猫吃鱼。。。。。。");
}
}
工厂类:
public class AnimalFactory{
private AnimalFactory{}
public static Animal creatAnimal(String name){
if("cat".equals(name)){
return new Cat();
}else{
return null;
}
}
}
测试类:
public class Test {
public static void main(String[] args) {
Animal a = AnimalFactory.creatAnimal("cat");
a.eat();
}
}
模板设计模式:简单来说就是顶一个算法的骨架,而将具体算法延迟到子类中去实现。
/**
*
* 抽象的模板类
*
*/
public abstract class GetTime{
public Long getTime(){
long start = System.currentTimeMillis();
code();
long end = System.currentTimeMillis();
return end-start;
}
public abstract void code();
}
模板类的实现类:
public class GetTimeImpl implements GetTime{
@Override
public void code(){
System.out.println("这里面写的code()方法的具体实现!!");
}
}
单例模式:确保在内存中只有一个对象,该实例必须自动创建,并且对外提供。
/**
*饿汉单例
*/
public class Singleton {
private Singleton(){}
private static Singleton singleton = new Singleton();
public static Singleton getSingleton(){
return singleton;
}
}
懒汉单例:
/**
*懒汉单例
*/
public class Singleton {
private Singleton(){}
private static Singleton singleton = null;
public static Singleton getSingleton(){
if(singleton==null){
singleton = new Singleton();
}
return singleton;
}
}
这种单例模式容易出现线程安全问题,所以得加上同步锁解决。
/**
*懒汉单例,加同步锁
*/
public class Singleton {
private Singleton(){}
//这里要加上volatile关键字,不加的话,虚拟机在执行下边创建实例的时候可能会发生重排序,导致返回一个未完成初始化的实例。
private static volatile Singleton singleton = null;
public static Singleton getSingleton(){
if(singleton==null){
synchronized(Singleton.class){
if(singleton==null)
/**
* 创建对象为三步:1,分配对象内存空间
* 2,初始化对象
* 3,设置singleton指向刚分配的内存地址
* 如果发生重排序,可能2和3的顺序发生颠倒,导致其他线程判断singleton不为null的时候,仍为完成初始化
* 返回一个不能正常使用的singleton
*/
singleton = new Singleton();
}
}
return singleton;
}
}
上边的单例均有可能通过反射来破坏,最安全的是通过内部静态枚举方法来实现,因为jvm保证enum不能被反射,并且构造器只执行一次
enum Singleton {
INSTANCE{
public void getSomething(){
System.out.println("Something is here!");
};
};
public abstract void getSomething();
}
到这里就总结完了,这也是最近学到的,,在这里做个笔记,继续学习!!