JAVA设计模式简介及八种常见设计模式详解

JAVA设计模式简介及八种常见设计模式详解

一、什么是设计模式

设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被广泛应用的原因。简单说:

模式:在某些场景下,针对某类问题的某种通用的解决方案。

场景:项目所在的环境

问题:约束条件,项目目标等

解决方案:通用、可复用的设计,解决约束达到目标。

二、设计模式的六大原则

因为设计模式就是基于这些原则的实现,所以很有必要了解这些原则,下面主要对面向对象编程的几个原则进行简单介绍。

1、开闭原则(Open Close Principle)

开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。

2、里氏代换原则(Liskov Substitution Principle)

里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。

3、依赖倒转原则(Dependence Inversion Principle)

这个是开闭原则的基础,具体内容:真对接口编程,依赖于抽象而不依赖于具体。

4、接口隔离原则(Interface Segregation Principle)

这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。

5、迪米特法则(最少知道原则)(Demeter Principle)

为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。

6、合成复用原则(Composite Reuse Principle)

原则是尽量使用合成/聚合的方式,而不是使用继承。

三、设计模式的三个分类

创建型模式:对象实例化的模式,创建型模式用于解耦对象的实例化过程。

结构型模式:把类或对象结合在一起形成一个更大的结构。

行为型模式:类和对象如何交互,及划分责任和算法。

如下图所示:

四、各分类中模式的关键点

创建型模式

单例模式:某个类只能有一个实例,提供一个全局的访问点。

简单工厂:一个工厂类根据传入的参量决定创建出那一种产品类的实例。

工厂方法:定义一个创建对象的接口,让子类决定实例化那个类。

抽象工厂:创建相关或依赖对象的家族,而无需明确指定具体类。

建造者模式:封装一个复杂对象的构建过程,并可以按步骤构造。

原型模式:通过复制现有的实例来创建新的实例。

结构性模式

适配器模式:将一个类的方法接口转换成客户希望的另外一个接口。

组合模式:将对象组合成树形结构以表示“”部分-整体“”的层次结构。

装饰模式:动态的给对象添加新的功能。

代理模式:为其他对象提供一个代理以便控制这个对象的访问。

亨元(蝇量)模式:通过共享技术来有效的支持大量细粒度的对象。

外观模式:对外提供一个统一的方法,来访问子系统中的一群接口。

桥接模式:将抽象部分和它的实现部分分离,使它们都可以独立的变化。

行为型模式

模板模式:定义一个算法结构,而将一些步骤延迟到子类实现。

解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器。

策略模式:定义一系列算法,把他们封装起来,并且使它们可以相互替换。

状态模式:允许一个对象在其对象内部状态改变时改变它的行为。

观察者模式:对象间的一对多的依赖关系。

备忘录模式:在不破坏封装的前提下,保持对象的内部状态。

中介者模式:用一个中介对象来封装一系列的对象交互。

命令模式:将命令请求封装为一个对象,使得可以用不同的请求来进行参数化。

访问者模式:在不改变数据结构的前提下,增加作用于一组对象元素的新功能。

责任链模式:将请求的发送者和接收者解耦,使的多个对象都有处理这个请求的机会。

迭代器模式:一种遍历访问聚合对象中各个元素的方法,不暴露该对象的内部结构。

五、八种常见设计模式详解

1、单例模式

所谓的单例设计指的是一个类只允许产生一个实例化对象。 最好理解的一种设计模式,分为懒汉式和饿汉式。

1.1、饿汉式

——构造方法私有化,外部无法产生新的实例化对象,只能通过static方法取得实例化对象

class Singleton {

/**

* 在类的内部可以访问私有结构,所以可以在类的内部产生实例化对象

*/

private static Singleton instance = new Singleton();

/**

* private 声明构造

*/

private Singleton() {

}

/**

* 返回对象实例

*/

public static Singleton getInstance() {

return instance;

}

public void print() {

System.out.println("Hello Singleton...");

}

}

1.2、懒汉式

——当第一次去使用Singleton对象的时候才会为其产生实例化对象的操作

class Singleton {

/**

* 声明变量

*/

private static volatile Singleton singleton = null;

/**

* 私有构造方法

*/

private Singleton() {

}

/**

* 提供对外方法

* @return

*/

public static Singleton getInstance() {

// 还未实例化

if (singleton == null) {

synchronized (Singleton.class) {

if (singleton == null) {

singleton = new Singleton();

}

}

}

return singleton;

}

public void print() {

System.out.println("Hello World");

}

}

当多个线程并发执行 getInstance 方法时,懒汉式会存在线程安全问题,所以用到了 synchronized 来实现线程的同步,当一个线程获得锁的时候其他线程就只能在外等待其执行完毕。而饿汉式则不存在线程安全的问题。

2、工厂设计模式

工厂模式分为工厂方法模式和抽象工厂模式。

工厂方法模式

工厂方法模式:

1. 工厂方法模式分为三种:普通工厂模式,就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。

2. 多个工厂方法模式,是对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。

3. 静态工厂方法模式,将上面的多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可。

2.1、普通工厂模式

建立一个工厂类,对实现了同一接口的一些类进行实例的创建。

interface Sender {

void Send();

}

class MailSender implements Sender {

@Override

public void Send() {

System.out.println("This is mail sender...");

}

}

class SmsSender implements Sender {

@Override

public void Send() {

System.out.println("This is sms sender...");

}

}

public class FactoryPattern {

public static void main(String[] args) {

Sender sender = produce("mail");

sender.Send();

}

public static Sender produce(String str) {

if ("mail".equals(str)) {

return new MailSender();

} else if ("sms".equals(str)) {

return new SmsSender();

} else {

System.out.println("输入错误...");

return null;

}

}

}

2.2、 多个工厂方法模式

该模式是对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。

interface Sender {

void Send();

}

class MailSender implements Sender {

@Override

public void Send() {

System.out.println("This is mail sender...");

}

}

class SmsSender implements Sender {

@Override

public void Send() {

System.out.println("This is sms sender...");

}

}

class SendFactory {

public Sender produceMail() {

return new MailSender();

}

public Sender produceSms() {

return new SmsSender();

}

}

public class FactoryPattern {

public static void main(String[] args) {

SendFactory factory = new SendFactory();

Sender sender = factory.produceMail();

sender.Send();

}

}

2.3、 静态工厂方法模式

将上面的多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可。

interface Sender {

void Send();

}

class MailSender implements Sender {

@Override

public void Send() {

System.out.println("This is mail sender...");

}

}

class SmsSender implements Sender {

@Override

public void Send() {

System.out.println("This is sms sender...");

}

}

class SendFactory {

public static Sender produceMail() {

return new MailSender();

}

public static Sender produceSms() {

return new SmsSender();

}

}

public class FactoryPattern {

public static void main(String[] args) {

Sender sender = SendFactory.produceMail();

sender.Send();

}

}

2.4、抽象工厂模式

工厂方法模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要扩展程序,必须对工厂类进行修改,这违背了闭包原则,所以,从设计角度考虑,有一定的问题,如何解决? 那么这就用到了抽象工厂模式,创建多个工厂类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。

interface Provider {

Sender produce();

}

interface Sender {

void Send();

}

class MailSender implements Sender {

public void Send() {

System.out.println("This is mail sender...");

}

}

class SmsSender implements Sender {

public void Send() {

System.out.println("This is sms sender...");

}

}

class SendMailFactory implements Provider {

public Sender produce() {

return new MailSender();

}

}

class SendSmsFactory implements Provider {

public Sender produce() {

return new SmsSender();

}

}

public class FactoryPattern {

public static void main(String[] args) {

Provider provider = new SendMailFactory();

Sender sender = provider.produce();

sender.Send();

}

}

3、 建造者模式

工厂类模式提供的是创建单个类的模式,而建造者模式则是将各种产品集中起来管理,用来创建复合对象,所谓复合对象就是指某个类具有不同的属性。

import java.util.ArrayList;

import java.util.List;

/**

* @Author: LiuWang

* @Created: 2018/8/6 17:47

*/

abstract class Builder {

/**

* 第一步:装CPU

*/

public abstract void buildCPU();

/**

* 第二步:装主板

*/

public abstract void buildMainBoard();

/**

* 第三步:装硬盘

*/

public abstract void buildHD();

/**

* 获得组装好的电脑

* @return

*/

public abstract Computer getComputer();

}

/**

* 装机人员装机

*/

class Director {

public void Construct(Builder builder) {

builder.buildCPU();

builder.buildMainBoard();

builder.buildHD();

}

}

/**

* 具体的装机人员

*/

class ConcreteBuilder extends Builder {

Computer computer = new Computer();

@Override

public void buildCPU() {

computer.Add("装CPU");

}

@Override

public void buildMainBoard() {

computer.Add("装主板");

}

@Override

public void buildHD() {

computer.Add("装硬盘");

}

@Override

public Computer getComputer() {

return computer;

}

}

class Computer {

/**

* 电脑组件集合

*/

private List parts = new ArrayList();

public void Add(String part) {

parts.add(part);

}

public void print() {

for (int i = 0; i < parts.size(); i++) {

System.out.println("组件:" + parts.get(i) + "装好了...");

}

System.out.println("电脑组装完毕...");

}

}

public class BuilderPattern {

public static void main(String[] args) {

Director director = new Director();

Builder builder = new ConcreteBuilder();

director.Construct(builder);

Computer computer = builder.getComputer();

computer.print();

}

}

4、 适配器设计模式

适配器模式是将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的的类的兼容性问题。

主要分三类:类的适配器模式、对象的适配器模式、接口的适配器模式。

4.1、 类的适配器模式:

class Source {

public void method1() {

System.out.println("This is original method...");

}

}

interface Targetable {

/**

* 与原类中的方法相同

*/

public void method1();

/**

* 新类的方法

*/

public void method2();

}

class Adapter extends Source implements Targetable {

@Override

public void method2() {

System.out.println("This is the targetable method...");

}

}

public class AdapterPattern {

public static void main(String[] args) {

Targetable targetable = new Adapter();

targetable.method1();

targetable.method2();

}

}

4.2、 对象的适配器模式

基本思路和类的适配器模式相同,只是将Adapter 类作修改,这次不继承Source 类,而是持有Source 类的实例,以达到解决兼容性的问题。

class Source {

public void method1() {

System.out.println("This is original method...");

}

}

interface Targetable {

/**

* 与原类中的方法相同

*/

public void method1();

/**

* 新类的方法

*/

public void method2();

}

class Wrapper implements Targetable {

private Source source;

public Wrapper(Source source) {

super();

this.source = source;

}

@Override

public void method1() {

source.method1();

}

@Override

public void method2() {

System.out.println("This is the targetable method...");

}

}

public class AdapterPattern {

public static void main(String[] args) {

Source source = new Source();

Targetable targetable = new Wrapper(source);

targetable.method1();

targetable.method2();

}

}

4.3、 接口的适配器模式

接口的适配器是这样的:有时我们写的一个接口中有多个抽象方法,当我们写该接口的实现类时,必须实现该接口的所有方法,这明显有时比较浪费,因为并不是所有的方法都是我们需要的,有时只需要某一些,此处为了解决这个问题,我们引入了接口的适配器模式,借助于一个抽象类,该抽象类实现了该接口,实现了所有的方法,而我们不和原始的接口打交道,只和该抽象类取得联系,所以我们写一个类,继承该抽象类,重写我们需要的方法就行。

/**

* 定义端口接口,提供通信服务

*/

interface Port {

/**

* 远程SSH端口为22

*/

void SSH();

/**

* 网络端口为80

*/

void NET();

/**

* Tomcat容器端口为8080

*/

void Tomcat();

/**

* MySQL数据库端口为3306

*/

void MySQL();

}

/**

* 定义抽象类实现端口接口,但是什么事情都不做

*/

abstract class Wrapper implements Port {

@Override

public void SSH() {

}

@Override

public void NET() {

}

@Override

public void Tomcat() {

}

@Override

public void MySQL() {

}

}

/**

* 提供聊天服务

* 需要网络功能

*/

class Chat extends Wrapper {

@Override

public void NET() {

System.out.println("Hello World...");

}

}

/**

* 网站服务器

* 需要Tomcat容器,Mysql数据库,网络服务,远程服务

*/

class Server extends Wrapper {

@Override

public void SSH() {

System.out.println("Connect success...");

}

@Override

public void NET() {

System.out.println("WWW...");

}

@Override

public void Tomcat() {

System.out.println("Tomcat is running...");

}

@Override

public void MySQL() {

System.out.println("MySQL is running...");

}

}

public class AdapterPattern {

private static Port chatPort = new Chat();

private static Port serverPort = new Server();

public static void main(String[] args) {

// 聊天服务

chatPort.NET();

// 服务器

serverPort.SSH();

serverPort.NET();

serverPort.Tomcat();

serverPort.MySQL();

}

}

5、 装饰模式

顾名思义,装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例。

interface Shape {

void draw();

}

/**

* 实现接口的实体类

*/

class Rectangle implements Shape {

@Override

public void draw() {

System.out.println("Shape: Rectangle...");

}

}

class Circle implements Shape {

@Override

public void draw() {

System.out.println("Shape: Circle...");

}

}

/**

* 创建实现了 Shape 接口的抽象装饰类。

*/

abstract class ShapeDecorator implements Shape {

protected Shape decoratedShape;

public ShapeDecorator(Shape decoratedShape) {

this.decoratedShape = decoratedShape;

}

@Override

public void draw() {

decoratedShape.draw();

}

}

/**

* 创建扩展自 ShapeDecorator 类的实体装饰类。

*/

class RedShapeDecorator extends ShapeDecorator {

public RedShapeDecorator(Shape decoratedShape) {

super(decoratedShape);

}

@Override

public void draw() {

decoratedShape.draw();

setRedBorder(decoratedShape);

}

private void setRedBorder(Shape decoratedShape) {

System.out.println("Border Color: Red");

}

}

/**

* 使用 RedShapeDecorator 来装饰 Shape 对象。

*/

public class DecoratorPattern {

public static void main(String[] args) {

Shape circle = new Circle();

Shape redCircle = new RedShapeDecorator(new Circle());

Shape redRectangle = new RedShapeDecorator(new Rectangle());

System.out.println("Circle with normal border");

circle.draw();

System.out.println("\nCircle of red border");

redCircle.draw();

System.out.println("\nRectangle of red border");

redRectangle.draw();

}

}

6、 策略模式

策略模式定义了一系列算法,并将每个算法封装起来,使他们可以相互替换,且算法的变化不会影响到使用算法的客户。需要设计一个接口,为一系列实现类提供统一的方法,多个实现类实现该接口,设计一个抽象类(可有可无,属于辅助类),提供辅助函数。策略模式的决定权在用户,系统本身提供不同算法的实现,新增或者删除算法,对各种算法做封装。因此,策略模式多用在算法决策系统中,外部用户只需要决定用哪个算法即可。

/**

* 抽象算法的策略类,定义所有支持的算法的公共接口

*/

abstract class Strategy {

/**

* 算法方法

*/

public abstract void AlgorithmInterface();

}

/**

* 具体算法A

*/

class ConcreteStrategyA extends Strategy {

//算法A实现方法

@Override

public void AlgorithmInterface() {

System.out.println("算法A的实现");

}

}

/**

* 具体算法B

*/

class ConcreteStrategyB extends Strategy {

/**

* 算法B实现方法

*/

@Override

public void AlgorithmInterface() {

System.out.println("算法B的实现");

}

}

/**

* 具体算法C

*/

class ConcreteStrategyC extends Strategy {

@Override

public void AlgorithmInterface() {

System.out.println("算法C的实现");

}

}

/**

* 上下文,维护一个对策略类对象的引用

*/

class Context {

Strategy strategy;

public Context(Strategy strategy) {

this.strategy = strategy;

}

public void contextInterface(){

strategy.AlgorithmInterface();

}

}

/**

* 客户端代码:实现不同的策略

*/

public class StrategyPattern {

public static void main(String[] args) {

Context context;

context = new Context(new ConcreteStrategyA());

context.contextInterface();

context = new Context(new ConcreteStrategyB());

context.contextInterface();

context = new Context(new ConcreteStrategyC());

context.contextInterface();

}

}

7、 代理模式

代理模式指给一个对象提供一个代理对象,并由代理对象控制对原对象的引用。代理可以分为静态代理和动态代理。通过代理模式,可以利用代理对象为被代理对象添加额外的功能,以此来拓展被代理对象的功能。可以用于计算某个方法执行时间,在某个方法执行前后记录日志等操作。

7.1、 静态代理

静态代理需要我们写出代理类和被代理类,而且一个代理类和一个被代理类一一对应。代理类和被代理类需要实现同一个接口,通过聚合使得代理对象中有被代理对象的引用,以此实现代理对象控制被代理对象的目的。

/**

* 代理类和被代理类共同实现的接口

*/

interface IService {

void service();

}

/**

* 被代理类

*/

class Service implements IService{

@Override

public void service() {

System.out.println("被代理对象执行相关操作");

}

}

/**

* 代理类

*/

class ProxyService implements IService{

/**

* 持有被代理对象的引用

*/

private IService service;

/**

* 默认代理Service类

*/

public ProxyService() {

this.service = new Service();

}

/**

* 也可以代理实现相同接口的其他类

* @param service

*/

public ProxyService(IService service) {

this.service = service;

}

@Override

public void service() {

System.out.println("开始执行service()方法");

service.service();

System.out.println("service()方法执行完毕");

}

}

//测试类

public class ProxyPattern {

public static void main(String[] args) {

IService service = new Service();

//传入被代理类的对象

ProxyService proxyService = new ProxyService(service);

proxyService.service();

}

}

7.2、 动态代理

JDK 1.3 之后,Java通过java.lang.reflect包中的三个类Proxy、InvocationHandler、Method来支持动态代理。动态代理常用于有若干个被代理的对象,且为每个被代理对象添加的功能是相同的(例如在每个方法运行前后记录日志)。动态代理的代理类不需要我们编写,由Java自动产生代理类源代码并进行编译最后生成代理对象。

创建动态代理对象的步骤: 1)指明一系列的接口来创建一个代理对象 2)创建一个调用处理器(InvocationHandler)对象 3)将这个代理指定为某个其他对象的代理对象 4) 在调用处理器的invoke()方法中采取代理,一方面将调用传递给真实对象,另一方面执行各种需要的操作

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

/**

* 代理类和被代理类共同实现的接口

*/

interface IService {

void service();

}

class Service implements IService{

@Override

public void service() {

System.out.println("被代理对象执行相关操作");

}

}

class ServiceInvocationHandler implements InvocationHandler {

/**

* 被代理的对象

*/

private Object srcObject;

public ServiceInvocationHandler(Object srcObject) {

this.srcObject = srcObject;

}

@Override

public Object invoke(Object proxyObj, Method method, Object[] args) throws Throwable {

System.out.println("开始执行"+method.getName()+"方法");

//执行原对象的相关操作,容易忘记

Object returnObj = method.invoke(srcObject,args);

System.out.println(method.getName()+"方法执行完毕");

return returnObj;

}

}

public class ProxyPattern {

public static void main(String[] args) {

IService service = new Service();

Class clazz = service.getClass();

IService proxyService = (IService) Proxy.newProxyInstance(clazz.getClassLoader(),

clazz.getInterfaces(), new ServiceInvocationHandler(service));

proxyService.service();

}

}

8、模板方法模式

定义:定义了一个算法的骨架,而将一些步骤延迟到子类中,模版方法使得子类可以在不改变算法结构的情况下,重新定义算法的步骤。

需求:简单描述一下:本公司有程序猿、测试、HR、项目经理等人,下面使用模版方法模式,记录下所有人员的上班情况

模板方法模式中的三类角色:

1、具体方法(Concrete Method)

2、抽象方法(Abstract Method)

3、钩子方法(Hook Method)

// 具体方法

public final void workOneDay() {

Log.e("workOneDay", "-----------------work start----------------");

enterCompany();

work();

exitCompany();

Log.e("workOneDay", "-----------------work end----------------");

}

// 工作 抽象方法

public abstract void work();

// 钩子方法

public boolean isNeedPrintDate() {

return false;

}

private void exitCompany() {

if (isNeedPrintDate()) {

Log.e("exitCompany", "---" + new Date().toLocaleString() + "--->");

}

Log.e("exitCompany", name + "---离开公司");

}

/**

* 重写父类的此方法,使可以查看离开公司时间

*/

@Override

public boolean isNeedPrintDate() {

return true;

}

最后测试:

查看所有人员的工作情况:

QAWorker qaWorker = new QAWorker("测试人员");

qaWorker();

HRWorker hrWorker = new HRWorker("莉莉姐");

hrWorker.workOneDay();...

查看程序猿离开公司的时间:

ITWorker itWorker = new ITWorker("jingbin");

itWorker.workOneDay();

参考:

https://www.cnblogs.com/pony1223/p/7608955.html

https://blog.csdn.net/gfuugff/article/details/86641373?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-4.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-4.channel_param

https://www.cnblogs.com/Diyo/p/11415588.html

https://blog.csdn.net/wmq880204/article/details/75106848

相关推荐

空调制冷剂多久加一次0
监控sh365下载

空调制冷剂多久加一次0

📅 06-29 👁️ 504
京东下单后多久发货?发货时间怎么规定的?
beat365唯一官网

京东下单后多久发货?发货时间怎么规定的?

📅 06-28 👁️ 5053
电信通话数据的保存多久
监控sh365下载

电信通话数据的保存多久

📅 06-29 👁️ 2350
江湖x恒山任务怎么做 恒山任务详解
beat365唯一官网

江湖x恒山任务怎么做 恒山任务详解

📅 06-29 👁️ 7506
仓鼠的寿命有多长?死前有什么征兆?
365bet滚球网

仓鼠的寿命有多长?死前有什么征兆?

📅 06-27 👁️ 2174
江西十大面积最大的城市排行榜
365bet滚球网

江西十大面积最大的城市排行榜

📅 06-29 👁️ 9255
《英雄联盟》传奇选手Perkz退役:G2功勋中单告别赛场,曾夺S9亚军+MSI冠军
江湖x恒山任务怎么做 恒山任务详解
beat365唯一官网

江湖x恒山任务怎么做 恒山任务详解

📅 06-29 👁️ 7506
仓鼠的寿命有多长?死前有什么征兆?
365bet滚球网

仓鼠的寿命有多长?死前有什么征兆?

📅 06-27 👁️ 2174