设计模式(自家学习用)
链接: link.
1、概述
设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。
总共有 23 种设计模式。这些模式可以分为三大类:创建型模式(Creational Patterns)、结构型模式(Structural Patterns)、行为型模式(Behavioral Patterns)。
创建型模式:
这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。包括:工厂模式、抽象工厂模式、单例模式、建造者模式、原型模式
结构型模式:
这些设计模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式。包括:适配器模式、桥接模式、过滤器模式、组合模式、装饰器模式、外观模式、享元模式、代理模式
行为型模式:
这些设计模式特别关注对象之间的通信。包括:责任链模式、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、空对象模式、策略模式、模板模式、访问者模式
补充:J2EE模式
包括:MVC 模式、业务代表模式、组合实体模式、数据访问对象模式、前端控制器模式、拦截过滤器模式、服务定位器模式、传输对象模式
这些设计模式特别关注表示层。这些模式是由 Sun Java Center 鉴定的。
OOP(面向对象程序设计)七大原则
开闭原则:对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码。
里式替换原则:继承必须保证父类所拥有的性质在子类中仍然成立。
依赖倒置原则:面向接口编程,不要面向实现编程。
单一职责原则:控制类的粒度大小、将对象解耦、提高内聚性。
接口隔离原则:要为各个类建立它们需要的专用接口。
迪米特法则:又称最少知道原则,是指一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。
合成复用原则:尽量使用合成/聚合的方式,而不是使用继承。
2、单例模式
链接: link.
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,不能通过new来创建该类的对象。(构造函数是私有的。)
常见的单利模式有:饿汉式、懒汉式、DCL双重检测锁、静态内部类以及枚举单例。但是以上单例方法除了枚举单例,其他的都可以通过反射进行破坏,因此都是不安全的。
注意:
①、单例类只能有一个实例。(构造函数是私有的。)
②、单例类必须自己创建自己的唯一实例。
③、单例类必须给所有其他对象提供访问这一实例的方法。
1、饿汉式 单例模式
问题:类加载时就创建对象,可能会造成内存空间的浪费
public class Hungry {
//构造器私有,防止外部用new来创建对象
private Hungry(){}
//饿汉式,在类加载的时候就创建对象
private static Hungry instance = new Hungry();
//对外提供访问对象的方法
public static Hungry getInstance(){
return instance;
}
}
2、非安全懒汉式 单例模式
需要时才进行创建
public class LazyMan {
private LazyMan(){}
private static LazyMan instance;
public static LazyMan getInstance(){
//需要时才进行创建,避免类加载就创建对象造成内存的浪费
if(instance==null){
instance = new LazyMan();
}
return instance;
}
}
3、安全版懒汉式 单例模式
问题:synchronized会影响效率
public class SafeLazyMan {
private SafeLazyMan(){}
private static SafeLazyMan instance;
//getInstance方法用synchronized修饰,保证安全
public static synchronized SafeLazyMan getInstance(){
if(instance == null){
instance = new SafeLazyMan();
}
return instance;
}
}
4、DCL双重检测锁+volatile 单例模式
注意为什么要给单例对象加volatile
public class DCL {
private DCL(){}
private volatile static DCL instance;
public static DCL getInstance(){
//双重检测锁
if(instance == null){
synchronized (DCL.class){
if (instance == null){
instance = new DCL(); //这里可能会发生指令重排,因此要使用volatile修饰 instance
/*
new一个对象不是原子性操作,分为三步:
1、内配内存空间
2、执行构造方法
3、把这个对象指向这个空间
例:如果线程A按照 1 3 2 的顺序进行了指令重排,
此时并发线程B发现instance不为null,就会直接执行return instance
但是此时的instance其实还没有完成构造,会造成问题。所以要禁止指令重排
*/
}
}
}
return instance;
}
}
5、使用静态内部类实现 单例模式
public class Outer {
private Outer(){}
//内部类在用到时才会加载,因此外部类在加载的时候并不会立即就加载内部类
private static class InnerClass{
private static final Outer instance = new Outer();
}
public static Outer getInstance(){
return InnerClass.instance;
}
}
6、枚举单例
不能通过反射破坏枚举单例
public enum EnumSingleton {
INSTANCE;
public EnumSingleton getInstance(){
return INSTANCE;
}
}
以上单例方法除了枚举单例,其他的都可以通过反射进行破坏
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class De_Singleton2 {
private static boolean flag = false;
//private De_Singleton2(){} //构造函数私有
private De_Singleton2(){
//防止反射破坏单例
synchronized (De_Singleton2.class){
if(flag == false){ //通过外部变量防止单例被破坏
flag = true;
}else{
throw new RuntimeException("不要试图通过反射破坏单例");
}
}
}
private volatile static De_Singleton2 instance;
public static De_Singleton2 getInstance(){
//双重检测锁
if(instance == null){
synchronized (De_Singleton2.class){
if (instance == null){
instance = new De_Singleton2();
}
}
}
return instance;
}
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//通过反射获得构造器
Constructor<De_Singleton2> constructor = De_Singleton2.class.getDeclaredConstructor(null);
//破坏构造器私有
constructor.setAccessible(true);
//两个对象都是通过构造器创建的
De_Singleton2 instance1 = constructor.newInstance();
De_Singleton2 instance2 = constructor.newInstance();
System.out.println(instance1);
System.out.println(instance2);
}
}
3、工厂模式
链接: link.
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
优点: 1、一个调用者想创建一个对象,只要知道其名称就可以了。 2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。 3、屏蔽产品的具体实现,调用者只关心产品的接口。
缺点:每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。
简单工厂模式:
//创建接口
public interface Car {
void name();
}
//接口实现类1
public class Tesla implements Car {
@Override
public void name() {
System.out.println("我是 Tesla!");
}
}
//接口实现类2
public class Wuling implements Car {
@Override
public void name() {
System.out.println("我是五菱!");
}
}
//工厂类,这是一个统一的工厂,所有类型的车都要通过该工厂获得
public class CarFactory {
public static Car getCar(String str){
if(str.equals("Tesla")){
return new Tesla();
}else if(str.equals("五菱")){
return new Wuling();
}else{
return null;
}
}
}
//消费者类
public class Consumer {
public static void main(String[] args) {
Car car1 = new CarFactory().getCar("Tesla");
car1.name();
Car car2 = new CarFactory().getCar("五菱");
car2.name();
}
}
工厂模式:
public interface Car {
void name();
}
public class Tesla implements Car {
@Override
public void name() {
System.out.println("我是 Tesla!");
}
}
public class Wuling implements Car {
@Override
public void name() {
System.out.println("我是五菱!");
}
}
//这是个统一的车工厂接口
public interface CarFactory {
Car getCar();
}
//每种车的工厂有自己的具体工厂实现
public class TeslaFactory implements CarFactory {
@Override
public Car getCar() {
return new Tesla();
}
}
public class WulingFactory implements CarFactory {
@Override
public Car getCar() {
return new Wuling();
}
}
public class Consumer {
public static void main(String[] args) {
//使用的时候,调用具体的工厂实现
Car car = new TeslaFactory().getCar();
car.name();
Car car1 = new WulingFactory().getCar();
car1.name();
}
}
4、抽象工厂模式
链接: link.
抽象工厂模式是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。
优点:当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。
缺点:产品族扩展非常困难,要增加一个系列的某一产品,既要在抽象的 Creator 里加代码,又要在具体的里面加代码。
//手机产品 接口
public interface MoilePhone {
void call();
void sms();
}
//手机产品的具体实现类
public class Xiaomi implements MoilePhone {
@Override
public void call() {
System.out.println("小米,为发烧而生~");
}
@Override
public void sms() {
System.out.println("Are you OK ?");
}
}
public class Huawei implements MoilePhone {
@Override
public void call() {
System.out.println("爱国就买华为手机!");
}
@Override
public void sms() {
System.out.println("年少有为!");
}
}
//路由器产品接口
public interface Router {
void start();
void shutdown();
}
//路由器产品具体实现类
public class XiaomiRouter implements Router {
@Override
public void start() {
System.out.println("小米路由器开机啦!");
}
@Override
public void shutdown() {
System.out.println("小米路由器关机机啦!");
}
}
public class HuaweiRouter implements Router {
@Override
public void start() {
System.out.println("华为路由器开机啦!");
}
@Override
public void shutdown() {
System.out.println("华为路由器关机机啦!");
}
}
//超级工厂接口
public interface Factory {
//生产手机业务
MoilePhone mobilePhone();
//生产路由器业务
Router router();
}
//超级工厂接口的具体工厂实现类
public class XiaomiFactory implements Factory {
@Override
public MoilePhone mobilePhone() {
return new Xiaomi();
}
@Override
public Router router() {
return new XiaomiRouter();
}
}
public class HuaweiFactory implements Factory {
@Override
public MoilePhone mobilePhone() {
return new Huawei();
}
@Override
public Router router() {
return new HuaweiRouter();
}
}
//测试类
public class Consumer {
public static void main(String[] args) {
System.out.println("=============================小米公司产品================================");
XiaomiFactory xiaomiFactory = new XiaomiFactory();
MoilePhone xiaomi = xiaomiFactory.mobilePhone();
xiaomi.call();
xiaomi.sms();
Router xiaomirouter = xiaomiFactory.router();
xiaomirouter.start();
xiaomirouter.shutdown();
System.out.println("=============================华为公司产品================================");
HuaweiFactory huaweiFactory = new HuaweiFactory();
MoilePhone huawei = huaweiFactory.mobilePhone();
huawei.call();
huawei.sms();
Router huaweirouter = huaweiFactory.router();
huaweirouter.start();
huaweirouter.shutdown();
}
}