Java面试成神-07

  • 什么是设计模式

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

  • 模式:在某些场景下,针对某类问题的某种通用的解决方案。
  • 场景:项目所在的环境
  • 问题:约束条件,项目目标等
  • 解决方案:通用、可复用的设计,解决约束达到目标。
  • java的设计模式大体上分为三大类:
  • 创建型模式(5种):
    工厂方法模式,抽象工厂模式,单例模式,建造者模式,原型模式。
  • 结构型模式(7种):
    适配器模式,装饰器模式,代理模式,外观模式,桥接模式,组合模式,享元模式。
  • 行为型模式(11种):
    策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
  • 设计模式遵循的原则有6个:

1、开闭原则(Open Close Principle)
  对扩展开放,对修改关闭。
2、里氏代换原则(Liskov Substitution Principle)
  只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。
3、依赖倒转原则(Dependence Inversion Principle)
  这个是开闭原则的基础,对接口编程,依赖于抽象而不依赖于具体。
4、接口隔离原则(Interface Segregation Principle)
  使用多个隔离的借口来降低耦合度。
5、迪米特法则(最少知道原则)(Demeter Principle)
  一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。
6、合成复用原则(Composite Reuse Principle)
  原则是尽量使用合成/聚合的方式,而不是使用继承。继承实际上破坏了类的封装性,超类的方法可能会被子类修改。

  • 各分类中模式的关键点
序号 名称 内容
1 单例模式 某个类只能有一个实例,提供一个全局的访问点。
2 迭代器模式 一种遍历访问聚合对象中各个元素的方法,不暴露该对象的内部结构。
3 工厂方法 定义一个创建对象的接口,让子类决定实例化那个类。
4 抽象工厂 创建相关或依赖对象的家族,而无需明确指定具体类。
5 建造者模式 封装一个复杂对象的构建过程,并可以按步骤构造。
6 原型模式 通过复制现有的实例来创建新的实例。
7 适配器模式 将一个类的方法接口转换成客户希望的另外一个接口。
8 组合模式 将对象组合成树形结构以表示“”部分-整体“”的层次结构。
9 装饰模式 动态的给对象添加新的功能。
10 代理模式 为其他对象提供一个代理以便控制这个对象的访问。
11 亨元模式 通过共享技术来有效的支持大量细粒度的对象。
12 外观模式 对外提供一个统一的方法,来访问子系统中的一群接口。
13 桥接模式 将抽象部分和它的实现部分分离,使它们都可以独立的变化。
14 模板模式 定义一个算法结构,而将一些步骤延迟到子类实现。
15 解释器模式 给定一个语言,定义它的文法的一种表示,并定义一个解释器。
16 策略模式 定义一系列算法,把他们封装起来,并且使它们可以相互替换。
17 状态模式 允许一个对象在其对象内部状态改变时改变它的行为。
18 观察者模式 对象间的一对多的依赖关系。
19 备忘录模式 在不破坏封装的前提下,保持对象的内部状态。
20 中介者模式 用一个中介对象来封装一系列的对象交互。
21 命令模式 将命令请求封装为一个对象,使得可以用不同的请求来进行参数化。
22 访问者模式 在不改变数据结构的前提下,增加作用于一组对象元素的新功能。
23 责任链模式 将请求的发送者和接收者解耦,使的多个对象都有处理这个请求的机会。
  • 单例模式
  • 单例模式,它的定义就是确保某一个类只有一个实例,并且提供一个全局访问点。
  • 单例模式具备典型的3个特点:1、只有一个实例。 2、自我实例化。 3、提供全局访问点。
  • 因此当系统中只需要一个实例对象或者系统中只允许一个公共访问点,除了这个公共访问点外,不能通过其他访问点访问该实例时,可以使用单例模式。
  • 单例模式的主要优点就是节约系统资源、提高了系统效率,同时也能够严格控制客户对它的访问。也许就是因为系统中只有一个实例,这样就导致了单例类的职责过重,违背了“单一职责原则”,同时也没有抽象类,所以扩展起来有一定的困难。
public class Singleton {
   private Singleton(){}

   private static class SingletonBuild{
       private static Singleton value = new Singleton();
   }

   public Singleton getInstance(){  return  SingletonBuild.value ;}
   
}
  • 工厂方法模式
  • 作为抽象工厂模式的孪生兄弟,工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个,也就是说工厂方法模式让实例化推迟到子类。
  • 工厂方法模式非常符合“开闭原则”,当需要增加一个新的产品时,我们只需要增加一个具体的产品类和与之对应的具体工厂即可,无须修改原有系统。同时在工厂方法模式中用户只需要知道生产产品的具体工厂即可,无须关系产品的创建过程,甚至连具体的产品类名称都不需要知道。虽然他很好的符合了“开闭原则”,但是由于每新增一个新产品时就需要增加两个类,这样势必会导致系统的复杂度增加。
interface food{}

class A implements food{}
class B implements food{}
class C implements food{}

public class StaticFactory {

   private StaticFactory(){}
   
   public static food getA(){  return new A(); }
   public static food getB(){  return new B(); }
   public static food getC(){  return new C(); }
}

class Client{
   //客户端代码只需要将相应的参数传入即可得到对象
   //用户不需要了解工厂类内部的逻辑。
   public void get(String name){
       food x = null ;
       if ( name.equals("A")) {
           x = StaticFactory.getA();
       }else if ( name.equals("B")){
           x = StaticFactory.getB();
       }else {
           x = StaticFactory.getC();
       }
   }
}
  • 抽象工厂模式
  • 所谓抽象工厂模式就是提供一个接口,用于创建相关或者依赖对象的家族,而不需要明确指定具体类。他允许客户端使用抽象的接口来创建一组相关的产品,而不需要关系实际产出的具体产品是什么。这样一来,客户就可以从具体的产品中被解耦。它的优点是隔离了具体类的生成,使得客户端不需要知道什么被创建了,而缺点就在于新增新的行为会比较麻烦,因为当添加一个新的产品对象时,需要更加需要更改接口及其下所有子类。
interface food{}

class A implements food{}
class B implements food{}

interface produce{ food get();}

class FactoryForA implements produce{
   @Override
   public food get() {
       return new A();
   }
}
class FactoryForB implements produce{
   @Override
   public food get() {
       return new B();
   }
}
public class AbstractFactory {
   public void ClientCode(String name){
       food x= new FactoryForA().get();
       x = new FactoryForB().get();
   }
}
  • 建造者模式
  • 对于建造者模式而言,它主要是将一个复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示。适用于那些产品对象的内部结构比较复杂。
  • 建造者模式将复杂产品的构建过程封装分解在不同的方法中,使得创建过程非常清晰,能够让我们更加精确的控制复杂产品对象的创建过程,同时它隔离了复杂产品对象的创建和使用,使得相同的创建过程能够创建不同的产品。但是如果某个产品的内部结构过于复杂,将会导致整个系统变得非常庞大,不利于控制,同时若几个产品之间存在较大的差异,则不适用建造者模式,毕竟这个世界上存在相同点大的两个产品并不是很多,所以它的使用范围有限。
public class Builder {

   static class Student{
       String name = null ;
       int number = -1 ;
       String sex = null ;
       int age = -1 ;
       String school = null ;

     //构建器,利用构建器作为参数来构建Student对象
       static class StudentBuilder{
           String name = null ;
           int number = -1 ;
           String sex = null ;
           int age = -1 ;
           String school = null ;
           public StudentBuilder setName(String name) {
               this.name = name;
               return  this ;
           }

          public StudentBuilder setNumber(int number) {
               this.number = number;
               return  this ;
           }

           public StudentBuilder setSex(String sex) {
               this.sex = sex;
               return  this ;
           }

           public StudentBuilder setAge(int age) {
               this.age = age;
               return  this ;
           }

           public StudentBuilder setSchool(String school) {
               this.school = school;
               return  this ;
           }
           public Student build() {
               return new Student(this);
           }
       }

       public Student(StudentBuilder builder){
           this.age = builder.age;
           this.name = builder.name;
           this.number = builder.number;
           this.school = builder.school ;
           this.sex = builder.sex ;
       }
   }

   public static void main( String[] args ){
       Student a = new Student.StudentBuilder().setAge(13).setName("LiHua").build();
       Student b = new Student.StudentBuilder().setSchool("sc").setSex("Male").setName("ZhangSan").build();
   }
}
  • 原型模式
  • 在我们应用程序可能有某些对象的结构比较复杂,但是我们又需要频繁的使用它们,如果这个时候我们来不断的新建这个对象势必会大大损耗系统内存的,这个时候我们需要使用原型模式来对这个结构复杂又要频繁使用的对象进行克隆。所以原型模式就是用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。
  • 它主要应用与那些创建新对象的成本过大时。它的主要优点就是简化了新对象的创建过程,提高了效率,同时原型模式提供了简化的创建结构。
public class Prototype implements Cloneable{

   private String name;

   public String getName() {
       return name;
   }

   public void setName(String name) {
       this.name = name;
   }

   @Override
   protected Object clone()   {
       try {
           return super.clone();
       } catch (CloneNotSupportedException e) {
           e.printStackTrace();
       }finally {
           return null;
       }
   }

   public static void main ( String[] args){
       Prototype pro = new Prototype();
       Prototype pro1 = (Prototype)pro.clone();
   }
}
  • 适配器模式
  • 在我们的应用程序中我们可能需要将两个不同接口的类来进行通信,在不修改这两个的前提下我们可能会需要某个中间件来完成这个衔接的过程。这个中间件就是适配器。所谓适配器模式就是将一个类的接口,转换成客户期望的另一个接口。它可以让原本两个不兼容的接口能够无缝完成对接。
  • 作为中间件的适配器将目标类和适配者解耦,增加了类的透明性和可复用性。
  • 适配器模式的作用就是在原来的类上提供新功能。主要可分为3种:
//类适配:创建新类,继承源类,并实现新接口,例如 
class  adapter extends oldClass  implements newFunc{}
//对象适配:创建新类持源类的实例,并实现新接口,例如 
class adapter implements newFunc { private oldClass oldInstance ;}
//接口适配:创建新的抽象类实现旧接口方法。例如 
abstract class adapter implements oldClassFunc { void newFunc();}
  • 桥接模式
  • 如果说某个系统能够从多个角度来进行分类,且每一种分类都可能会变化,那么我们需要做的就是讲这多个角度分离出来,使得他们能独立变化,减少他们之间的耦合,这个分离过程就使用了桥接模式。所谓桥接模式就是讲抽象部分和实现部分隔离开来,使得他们能够独立变化。
  • 桥接模式将继承关系转化成关联关系,封装了变化,完成了解耦,减少了系统中类的数量,也减少了代码量。
interface DrawAPI {
   public void drawCircle(int radius, int x, int y);
}
class RedCircle implements DrawAPI {
   @Override
   public void drawCircle(int radius, int x, int y) {
       System.out.println("Drawing Circle[ color: red, radius: "
               + radius +", x: " +x+", "+ y +"]");
   }
}
class GreenCircle implements DrawAPI {
   @Override
   public void drawCircle(int radius, int x, int y) {
       System.out.println("Drawing Circle[ color: green, radius: "
               + radius +", x: " +x+", "+ y +"]");
   }
}

abstract class Shape {
   protected DrawAPI drawAPI;
   protected Shape(DrawAPI drawAPI){
       this.drawAPI = drawAPI;
   }
   public abstract void draw();
}

class Circle extends Shape {
   private int x, y, radius;

   public Circle(int x, int y, int radius, DrawAPI drawAPI) {
       super(drawAPI);
       this.x = x;
       this.y = y;
       this.radius = radius;
   }

   public void draw() {
       drawAPI.drawCircle(radius,x,y);
   }
}

//客户端使用代码
Shape redCircle = new Circle(100,100, 10, new RedCircle());
Shape greenCircle = new Circle(100,100, 10, new GreenCircle());
redCircle.draw();
greenCircle.draw();
  • 组合模式
  • 组合模式组合多个对象形成树形结构以表示“整体-部分”的结构层次。它定义了如何将容器对象和叶子对象进行递归组合,使得客户在使用的过程中无须进行区分,可以对他们进行一致的处理。在使用组合模式中需要注意一点也是组合模式最关键的地方:叶子对象和组合对象实现相同的接口。这就是组合模式能够将叶子节点和对象节点进行一致处理的原因。
  • 虽然组合模式能够清晰地定义分层次的复杂对象,也使得增加新构件也更容易,但是这样就导致了系统的设计变得更加抽象,如果系统的业务规则比较复杂的话,使用组合模式就有一定的挑战了。
abstract class component{}

class File extends  component{ String filename;}

class Folder extends  component{
   component[] files ;  //既可以放文件File类,也可以放文件夹Folder类。>Folder类下又有子文件或子文件夹。
   String foldername ;
   public Folder(component[] source){ files = source ;}
   
   public void scan(){
       for ( component f:files){
           if ( f instanceof File){
               System.out.println("File "+((File) f).filename);
           }else if(f instanceof Folder){
               Folder e = (Folder)f ;
               System.out.println("Folder "+e.foldername);
               e.scan();
           }
       }
   }
   
}
  • 装饰模式
  • 我们可以通过继承和组合的方式来给一个对象添加行为,虽然使用继承能够很好拥有父类的行为,但是它存在几个缺陷:一、对象之间的关系复杂的话,系统变得复杂不利于维护。二、容易产生“类爆炸”现象。三、是静态的。在这里我们可以通过使用装饰者模式来解决这个问题。
  • 装饰者模式,动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更加有弹性的替代方案。虽然装饰者模式能够动态将责任附加到对象上,但是他会产生许多的细小对象,增加了系统的复杂度。
interface Source{ void method();}
public class Decorator implements Source{

   private Source source ;
   public void decotate1(){
       System.out.println("decorate");
   }
   @Override
   public void method() {
       decotate1();
       source.method();
   }
}
  • 外观模式
  • 我们都知道类与类之间的耦合越低,那么可复用性就越好,如果两个类不必彼此通信,那么就不要让这两个类发生直接的相互关系,如果需要调用里面的方法,可以通过第三者来转发调用。外观模式非常好的诠释了这段话。外观模式提供了一个统一的接口,用来访问子系统中的一群接口。它让一个应用程序中子系统间的相互依赖关系减少到了最少,它给子系统提供了一个简单、单一的屏障,客户通过这个屏障来与子系统进行通信。通过使用外观模式,使得客户对子系统的引用变得简单了,实现了客户与子系统之间的松耦合。但是它违背了“开闭原则”,因为增加新的子系统可能需要修改外观类或客户端的源代码。
public class Facade {
   private subSystem1 subSystem1 = new subSystem1();
   private subSystem2 subSystem2 = new subSystem2();
   private subSystem3 subSystem3 = new subSystem3();
   
   public void startSystem(){
       subSystem1.start();
       subSystem2.start();
       subSystem3.start();
   }
   
   public void stopSystem(){
       subSystem1.stop();
       subSystem2.stop();
       subSystem3.stop();
   }
}
  • 亨元模式
  • 在一个系统中对象会使得内存占用过多,特别是那些大量重复的对象,这就是对系统资源的极大浪费。享元模式对对象的重用提供了一种解决方案,它使用共享技术对相同或者相似对象实现重用。享元模式就是运行共享技术有效地支持大量细粒度对象的复用。系统使用少量对象,而且这些都比较相似,状态变化小,可以实现对象的多次复用。这里有一点要注意:享元模式要求能够共享的对象必须是细粒度对象。享元模式通过共享技术使得系统中的对象个数大大减少了,同时享元模式使用了内部状态和外部状态,同时外部状态相对独立,不会影响到内部状态,所以享元模式能够使得享元对象在不同的环境下被共享。同时正是分为了内部状态和外部状态,享元模式会使得系统变得更加复杂,同时也会导致读取外部状态所消耗的时间过长。
abstract class flywei{ }

public class Flyweight extends flywei{
   Object obj ;
   public Flyweight(Object obj){
       this.obj = obj;
   }
}

class  FlyweightFactory{
   private HashMap<Object,Flyweight> data;

   public FlyweightFactory(){ data = new HashMap<>();}

   public Flyweight getFlyweight(Object object){
       if ( data.containsKey(object)){
           return data.get(object);
       }else {
           Flyweight flyweight = new Flyweight(object);
           data.put(object,flyweight);
           return flyweight;
       }
   }
}
  • .代理模式
  • 代理模式就是给一个对象提供一个代理,并由代理对象控制对原对象的引用。它使得客户不能直接与真正的目标对象通信。代理对象是目标对象的代表,其他需要与这个目标对象打交道的操作都是和这个代理对象在交涉。
  • 代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了的作用和保护了目标对象的,同时也在一定程度上面减少了系统的耦合度。
interface Source{ void method();}

class OldClass implements Source{
   @Override
   public void method() {
   }
}

class Proxy implements Source{
   private Source source = new OldClass();

   void doSomething(){}
   @Override
   public void method() {
       new Class1().Func1();
       source.method();
       new Class2().Func2();
       doSomething();
   }
}
  • 访问者模式
  • 访问者模式俗称23大设计模式中最难的一个。除了结构复杂外,理解也比较难。在我们软件开发中我们可能会对同一个对象有不同的处理,如果我们都做分别的处理,将会产生灾难性的错误。对于这种问题,访问者模式提供了比较好的解决方案。访问者模式即表示一个作用于某对象结构中的各元素的操作,它使我们可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
  • 访问者模式的目的是封装一些施加于某种数据结构元素之上的操作,一旦这些操作需要修改的话,接受这个操作的数据结构可以保持不变。为不同类型的元素提供多种访问操作方式,且可以在不修改原有系统的情况下增加新的操作方式。同时我们还需要明确一点那就是访问者模式是适用于那些数据结构比较稳定的,因为他是将数据的操作与数据结构进行分离了,如果某个系统的数据结构相对稳定,但是操作算法易于变化的话,就比较适用适用访问者模式,因为访问者模式使得算法操作的增加变得比较简单了。
interface Service {

   public void accept(Visitor visitor);
}

class Visitor {

   public void process(Service service) {
       // 基本业务
       System.out.println("基本业务");
   }

   public void process(Saving service) {
       // 存款
       System.out.println("存款");
   }

   public void process(Draw service) {
       // 提款
       System.out.println("提款");
   }

   public void process(Fund service) {
       System.out.println("基金");
       // 基金
   }

}

class Saving implements Service {

   public void accept(Visitor visitor) {
       visitor.process(this);

   }
}

class Draw implements Service {

   public void accept(Visitor visitor) {
       visitor.process(this);

   }
}

class Fund implements Service {

   public void accept(Visitor visitor) {
       visitor.process(this);

   }
}
  • 模板模式
  • 有些时候我们做某几件事情的步骤都差不多,仅有那么一小点的不同,在软件开发的世界里同样如此,如果我们都将这些步骤都一一做的话,费时费力不讨好。所以我们可以将这些步骤分解、封装起来,然后利用继承的方式来继承即可,当然不同的可以自己重写实现嘛!这就是模板方法模式提供的解决方案。
  • 所谓模板方法模式就是在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
  • 模板方法模式就是基于继承的代码复用技术的。在模板方法模式中,我们可以将相同部分的代码放在父类中,而将不同的代码放入不同的子类中。也就是说我们需要声明一个抽象的父类,将部分逻辑以具体方法以及具体构造函数的形式实现,然后声明一些抽象方法让子类来实现剩余的逻辑,不同的子类可以以不同的方式来实现这些逻辑。所以模板方法的模板其实就是一个普通的方法,只不过这个方法是将算法实现的步骤封装起来的。
package TemplateDemo;

/**
* 这是一个抽象的模板类
* */
abstract class AbstractClass {
    //定义一个模板方法来控制生产手机的流程
   public void makeMobile(){//TemplateMethod
      //首先生产手机体(模板方法自己完成)
      System.out.println("生产了一个手机body");
      this.makeDifferentFunction();
      this.makeDifferentColor();
      this.makeDifferentType();

      System.out.println("一部手机制作完成");
      //以上模板已经规定好了
   }

   //定义抽象的方法来完成不同的生产细节(不同功能的手机)
   protected abstract void makeDifferentFunction();
   //定义抽象的方法来完成不同的生产细节(不同颜色的手机)
   protected abstract void makeDifferentColor();
   //定义抽象的方法来完成不同的生产细节(不同类型的手机)
   protected abstract void makeDifferentType();

}

/**
* 创建真是的实现类来实现具体的生产细节
* */
class ConcreteClass extends AbstractClass{

   //实现父类中的抽象的方法
   protected void makeDifferentColor() {
      System.out.println("给该手机的颜色涂成蓝色");
   }

   protected void makeDifferentFunction() {
      System.out.println("给该手机实现看MP4的功能");
   }

   protected void makeDifferentType() {
       System.out.println("给该手机制作成翻盖的");   
   }

}

//写测试类

public class TestTemplate{
   public static void main(String args[]){
      AbstractClass ac = new ConcreteClass();//子类的对象付给父类
      ac.makeMobile();//制作手机
      //制作不同的手机只是修改子类就可以了
   }
}
  • 策略模式
  • 我们知道一件事可能会有很多种方式来实现它,但是其中总有一种最高效的方式,在软件开发的世界里面同样如此,我们也有很多中方法来实现一个功能,但是我们需要一种简单、高效的方式来实现它,使得系统能够非常灵活,这就是策略模式。
  • 所以策略模式就是定义了算法族,分别封装起来,让他们之前可以互相转换,此模式然该算法的变化独立于使用算法的客户。
  • 在策略模式中它将这些解决问题的方法定义成一个算法群,每一个方法都对应着一个具体的算法,这里的一个算法我就称之为一个策略。虽然策略模式定义了算法,但是它并不提供算法的选择,即什么算法对于什么问题最合适这是策略模式所不关心的,所以对于策略的选择还是要客户端来做。客户必须要清楚的知道每个算法之间的区别和在什么时候什么地方使用什么策略是最合适的,这样就增加客户端的负担。
  • 同时策略模式也非常完美的符合了“开闭原则”,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为。但是一个策略对应一个类将会是系统产生很多的策略类。
public interface Weapon {
    public void gun();
}

public class FirstGun implements Weapon {

   @Override
   public void gun() {
       System.out.println("使用AWM狙击步枪。");
   }

}

public class SecondGun implements Weapon {
   
   @Override
   public void gun() {
       System.out.println("使用S12K霰弹枪。");
   }
   
}

public class Context {
   Weapon weapon;
   
   public Context(Weapon weapon) { //构造函数
       super();
       this.weapon = weapon;
   }
   
   public Weapon getWeapon() { //get方法
       return weapon;
   }
   
   public void setWeapon(Weapon weapon) { //set方法
       this.weapon = weapon;
   }
   
   public void gun() {
       weapon.gun();
   }
}

public class StrategyClient {

   public static void main(String[] args) {
       //使用构造函数默认选择一把AWM狙击步枪(一个策略)
       Context context=new Context(new FirstGun());
       context.gun();
       
       //使用get、set方法切换到S12K霰弹枪(切换策略)
     System.out.println("------右前方30米发现敌人------");
       contex.set(new SecondGun());
       context.gun();
   }
}
  • 状态模式
  • 在很多情况下我们对象的行为依赖于它的一个或者多个变化的属性,这些可变的属性我们称之为状态,也就是说行为依赖状态,即当该对象因为在外部的互动而导致他的状态发生变化,从而它的行为也会做出相应的变化。对于这种情况,我们是不能用行为来控制状态的变化,而应该站在状态的角度来思考行为,即是什么状态就要做出什么样的行为。这个就是状态模式。
  • 所以状态模式就是允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。
  • 在状态模式中我们可以减少大块的if…else语句,它是允许态转换逻辑与状态对象合成一体,但是减少if…else语句的代价就是会换来大量的类,所以状态模式势必会增加系统中类或者对象的个数。
  • 同时状态模式是将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。但是这样就会导致系统的结构和实现都会比较复杂,如果使用不当就会导致程序的结构和代码混乱,不利于维护。
public class LuFei {
   public final static int ORDINARY = 0;//普通状态
   public final static int SECONDGEAR = 1;//二挡状态
   public final static int THIRDGEAR = 2;//三挡状态
   public final static int FOURTHGEAR = 3;//四挡状态
   private int state = ORDINARY;//由于路飞一开始是普通状态,所以我们初始化state为ORDINARY
   
   public void setstate(int state) {
       this.state = state;
   }
   
   public void change(){
       if(state == SECONDGEAR){
           System.out.println("路飞开启二挡战斗");
       }else if(state == THIRDGEAR){
           System.out.println("路飞开启三挡战斗");
       }else if(state == FOURTHGEAR){
           System.out.println("路飞开启四挡战斗");
       }else{
           System.out.println("路飞当前为普通状态战斗");
       }
   }
}

public class TestLuFei {
   public static void main(String[] args) {
       LuFei luFei = new LuFei();
       luFei.setstate(luFei.SECONDGEAR);
       luFei.change();
       luFei.setstate(luFei.THIRDGEAR);
       luFei.change();
       luFei.setstate(luFei.FOURTHGEAR);
       luFei.change();
       luFei.setstate(luFei.ORDINARY);
       luFei.change();
   }
}
  • 观察者模式
  • 何谓观察者模式?观察者模式定义了对象之间的一对多依赖关系,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并且自动更新。
  • 在这里,发生改变的对象称之为观察目标,而被通知的对象称之为观察者。一个观察目标可以对应多个观察者,而且这些观察者之间没有相互联系,所以么可以根据需要增加和删除观察者,使得系统更易于扩展。所以观察者提供了一种对象设计,让主题和观察者之间以松耦合的方式结合。
class House extends Observable{
   private double price;
   public House(double price){
       this.price=price;
   }
   public double getPrice(){
       return price;
   }
   public void setPrice(double price){
       if(this.price!=price){
           this.price=price;
           setChanged();  //标注价格已经被更改
           this.notifyObservers(price);  //通知观察者数据已被更改
       }
   }
   @Override
   public String toString() {
       return "当前房价为:"+price;
   }
}

class HousePriceObserver implements Observer{
   private String name;
   public  HousePriceObserver(String name) {
       this.name=name;
   }
   @Override
   public void update(Observable o, Object arg) {
       //这里最好判断一下通知是否来自于房价,有可能来自其它地方
       if(o instanceof House){
           System.out.println("购物者"+name+ "观察到房价已调整为:"+arg);
       }
   }
}

public class Test {
   public static void main(String[] args) {
       House house=new House(10000);
       HousePriceObserver A=new HousePriceObserver("A");
       HousePriceObserver B=new HousePriceObserver("B");
       HousePriceObserver C=new HousePriceObserver("C");
       house.addObserver(A);
       house.addObserver(B);
       house.addObserver(C);
       System.out.println(house);
       house.setPrice(6000);
       house.setPrice(8000);
   }
}
  • 备忘录模式
  • 后悔药人人都想要,但是事实却是残酷的,根本就没有后悔药可买,但是也不仅如此,在软件的世界里就有后悔药!备忘录模式就是一种后悔药,它给我们的软件提供后悔药的机制,通过它可以使系统恢复到某一特定的历史状态。
  • 所谓备忘录模式就是在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。它实现了对信息的封装,使得客户不需要关心状态保存的细节。保存就要消耗资源,所以备忘录模式的缺点就在于消耗资源。如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。
/**
* 游戏
*/
@Data
public class Game {

   /**
    * 玩家走的步数
    */
   private int playerStep;

   /**
    * 备份游戏
    * @return
    */
   public GameMemento createGameMemento(){
       return new GameMemento(playerStep);
   }

   /**
    * 开始玩游戏
    */
   public void play(){
       playerStep = 0;
   }

   /**
    * 恢复备份
    * @param gameMemento
    */
   public void restore(GameMemento gameMemento){
       this.playerStep = gameMemento.getPlayerSteps();
   }
}

/**
* 备份
*/
@Getter
public class GameMemento {
   /** 步数 */
   private int playerSteps;

   /**
    * 备份步数
    * @param playerSteps
    */
   public GameMemento(int playerSteps){
       this.playerSteps = playerSteps;
   }

}

/**
* 备份信息管理类
*/
public class Caretaker {
   /** 备份 */
   private GameMemento gameMemento;
   /** 恢复备份 */
   public GameMemento retrieveMemento(){
       return this.gameMemento;
   }
   /** 保存备份 */
   public void saveMemento(GameMemento gameMemento){
       this.gameMemento = gameMemento;
   }
}

public class Player {

   public static void main(String[] args) {

       Game game = new Game();
       System.out.println("游戏开始,捡到滑板,前进10步");
       game.setPlayerStep(10);
       //备份当前状态
       System.out.println("备份当前状态");
       GameMemento gameMemento = game.createGameMemento();

       Caretaker caretaker = new Caretaker();

       caretaker.saveMemento(gameMemento);
       System.out.println("备份完成");
       game.play();
       System.out.println("踩到便便了,当前步数为:"+game.getPlayerStep());
       System.out.println("还原到之前一步");
       game.restore(caretaker.retrieveMemento());

       System.out.println("恢复完成,当前玩家步数是:"+game.getPlayerStep());

   }

}
  • 中介者模式
  • 租房各位都有过的经历吧!在这个过程中中介结构扮演着很重要的角色,它在这里起到一个中间者的作用,给我们和房主互相传递信息。在外面软件的世界里同样需要这样一个中间者。在我们的系统中有时候会存在着对象与对象之间存在着很强、复杂的关联关系,如果让他们之间有直接的联系的话,必定会导致整个系统变得非常复杂,而且可扩展性很差!在前面我们就知道如果两个类之间没有不必彼此通信,我们就不应该让他们有直接的关联关系,如果实在是需要通信的话,我们可以通过第三者来转发他们的请求。同样,这里我们利用中介者来解决这个问题。
  • 所谓中介者模式就是用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。在中介者模式中,中介对象用来封装对象之间的关系,各个对象可以不需要知道具体的信息通过中介者对象就可以实现相互通信。它减少了对象之间的互相关系,提供了系统可复用性,简化了系统的结构。
  • 在中介者模式中,各个对象不需要互相知道了解,他们只需要知道中介者对象即可,但是中介者对象就必须要知道所有的对象和他们之间的关联关系,正是因为这样就导致了中介者对象的结构过于复杂,承担了过多的职责,同时它也是整个系统的核心所在,它有问题将会导致整个系统的问题。所以如果在系统的设计过程中如果出现“多对多”的复杂关系群时,千万别急着使用中介者模式,而是要仔细思考是不是您设计的系统存在问题。
@Slf4j
public abstract class Mediator {

   /**
    * 房东map
    */
   protected Map<People, Message> landlordMap = Maps.newHashMap();

   /**
    * 租户列表
    */
   protected List<People> renterList = Lists.newArrayList();

   /**
    * 注册房东信息
    *
    * @param landlord 房东
    * @param message  房屋信息
    */
   public void registerLandlord(People landlord, Message message) {
       landlordMap.put(landlord, message);
       log.info("中介信息:房东|{}|加入到中介平台,房屋信息:{}", landlord.getName(), message);
   }

   /**
    * 变更房东信息
    *
    * @param landlord 房东
    * @param message  房屋信息
    */
   protected void modifyLandlordInfo(People landlord, Message message) {
       landlordMap.put(landlord, message);
       log.info("中介信息:房东|{}|修改他在中介平台的房屋信息,现房屋信息:{}", landlord.getName(), message);
   }

   /**
    * 注册租户信息
    *
    * @param renter 租户
    */
   public void registerRenter(People renter) {
       renterList.add(renter);
       log.info("中介信息:租户|{}|来中介平台租房", renter.getName());
   }


   /**
    * 声明抽象方法 由具体中介者子类实现 消息的中转和协调
    */
   public abstract void operation(People people, Message message);

}

@Data
@AllArgsConstructor
@NoArgsConstructor
public abstract class People {

   private Mediator mediator;

   private String name;

   /**
    * 向中介发送消息
    */
   protected abstract void sendMessage(Message message);

   /**
    * 从中介获取消息
    */
   protected abstract void getMessage(Message message);
}

@Slf4j
public class Landlord extends People {

   public Landlord(Mediator mediator, String name) {
       super(mediator, name);
   }

   @Override
   protected void sendMessage(Message message) {
       getMediator().operation(this, message);
   }

   @Override
   protected void getMessage(Message message) {
       log.info("房东|{}|从中介获取到租户的信息:{}", getName(), message);
   }
}

@Slf4j
public class Renter extends People {

   public Renter(Mediator mediator, String name) {
       super(mediator, name);
   }

   @Override
   protected void sendMessage(Message message) {
       getMediator().operation(this, message);
   }

   @Override
   protected void getMessage(Message message) {
       log.info("租户|{}|从中介获取到房东的信息:{}", getName(), message);
   }
}

public class RealEstateAgent extends Mediator {

   @Override
   public void operation(People people, Message message) {
       if (people instanceof Renter) {
           // 将租户的租房条件信息发送给房东们
           landlordMap.keySet().forEach(landlord -> landlord.getMessage(message));

           // 租户收到中介那里房东的房屋信息
           landlordMap.values().forEach(messages -> people.getMessage(messages));
       } else if (people instanceof Landlord) {
           // 将房东的房屋信息发送给租户们
           renterList.forEach(renter -> renter.getMessage(message));
           // 变更中介里的房东房屋信息
           modifyLandlordInfo(people, message);
       }
   }
}

@Slf4j
public class Client {

   public static void main(String[] args) {
       Mediator mediator = new RealEstateAgent();
       People laoWang = new Landlord(mediator, "老王");
       People laoLee = new Landlord(mediator, "老李");
       People laoBai = new Landlord(mediator, "老白");

       People xiaoSan = new Renter(mediator, "小3");
       People xiaoWang = new Renter(mediator, "小王");

       mediator.registerLandlord(laoWang, Message.builder().msg("我这有2500的房子,市中心").build());
       mediator.registerLandlord(laoBai, Message.builder().msg("我这有2000的房子,地铁旁").build());
       mediator.registerLandlord(laoLee, Message.builder().msg("我这有2000的房子,落地阳台,大空间,采光好,地铁旁").build());

       mediator.registerRenter(xiaoSan);

       log.info("小3开始找房子");
       xiaoSan.sendMessage(Message.builder().msg("想找个月租2000块的房子,靠近地铁").build());
       log.info("没过多久---------老白升级了房屋信息");
       laoBai.sendMessage(Message.builder().msg("我这有2000的房子,地铁旁,我又加了空调和热水器").build());
       mediator.registerRenter(xiaoWang);
       log.info("小王开始找房子");
       xiaoWang.sendMessage(Message.builder().msg("想找个月租2500块的房子,靠近地铁").build());
       log.info("没过多久---------老李也升级了房屋信息");
       laoBai.sendMessage(Message.builder().msg("我这有2000的房子,落地阳台,大空间,采光好,地铁旁,我也加了空调和热水器").build());

   }
}
  • 迭代器模式
  • 对于迭代在编程过程中我们经常用到,能够游走于聚合内的每一个元素,同时还可以提供多种不同的遍历方式,这就是迭代器模式的设计动机。在我们实际的开发过程中,我们可能会需要根据不同的需求以不同的方式来遍历整个对象,但是我们又不希望在聚合对象的抽象接口中充斥着各种不同的遍历操作,于是我们就希望有某个东西能够以多种不同的方式来遍历一个聚合对象,这时迭代器模式出现了。
  • 何为迭代器模式?所谓迭代器模式就是提供一种方法顺序访问一个聚合对象中的各个元素,而不是暴露其内部的表示。迭代器模式是将迭代元素的责任交给迭代器,而不是聚合对象,我们甚至在不需要知道该聚合对象的内部结构就可以实现该聚合对象的迭代。
  • 通过迭代器模式,使得聚合对象的结构更加简单,它不需要关注它元素的遍历,只需要专注它应该专注的事情,这样就更加符合单一职责原则了。
//实现Iterable
class ScanAppleStore implements Iterable<String> {
 
 ArrayList<String> appleStore = new ArrayList<String>();
//为初始appleStore赋值
   ScanAppleStore() {
       Collections.addAll(appleStore, "Sweet", "Sour", "Bitter", "litter Sweet", "litter Sour", "litter Bitter");
       System.out.print(appleStore);
   }
 //重写Iterator方法
   @Override
   public Iterator<String> iterator() {
       return new Iterator<String>() {
           private int i = 0;

           public boolean hasNext() {
               if (i < appleStore.size()) {
                   return true;
               } else {
                   return false;
              }
           }

           public String next() {

               return appleStore.get(i++);
           }

           public void remove() {
               System.out.print("not defined!");
           }
       };
   }

   public Iterable<String> reverseIterator() {
       return new Iterable<String>() {
           public Iterator<String> iterator() {
               return new Iterator<String>() {
                   private int i = appleStore.size() - 1;

                   public boolean hasNext() {
                       if (i > -1) {
                           return true;
                       } else {
                           return false;
                       }
                   }

                   public String next() {

                       return appleStore.get(i--);
                   }

                   public void remove() {
                       System.out.print("not defined!");
                   }
               };
           }
       }; 
}
}

public class TestIterable {
 //构造函数初始化
   TestIterable() {
       ScanAppleStore appleTree = new ScanAppleStore();
//采用系统自带的迭代器
       System.out.println("采用系统自带的迭代器iterator:");
       for (String str : appleTree) {
           System.out.println(str);

       }
       System.out.println("======================");
       System.out.println("采用自己重新迭代器,让相反输出");
//采用自己重新迭代器,让相反输出
       for (String str : appleTree.reverseIterator()) {
           System.out.println(str);
       }
   }
   
   public  static void main(String[] args) {
       TestIterable a = new TestIterable();
       
   }
}
  • 解释器模式
  • 所谓解释器模式就是定义语言的文法,并且建立一个解释器来解释该语言中的句子。解释器模式描述了如何构成一个简单的语言解释器,主要应用在使用面向对象语言开发的编译器中。它描述了如何为简单的语言定义一个文法,如何在该语言中表示一个句子,以及如何解释这些句子。
@Getter
@Setter
public class Context {
   
   /**
    * 输入
    */
   private String input;
   /**
    * 结果
    */
   private int output;

   public Context(String input){
       this.input = input;
   }

   @Override
   public String toString() {
       return input + "=" + output;
   }
}

public abstract class Expression {

   Context context;

   /**
    * 解释一个给定的表达式
    * @param context
    */
   public abstract void interpret(Context context);

}

/**
* 垒加1
*/
public class MinusExpression extends Expression {

   /**
    * 解释一个给定的表达式
    *
    * @param context
    */
   @Override
   public void interpret(Context context) {

       this.context = context;
       String input = context.getInput();
       int in = Integer.valueOf(input);
       context.setOutput(in-1);

   }

   @Override
   public String toString() {
       return "--"+context.getInput()+"="+context.getOutput();
   }
}

/**
* 垒减
*/
public class PlusExpression extends Expression {

   /**
    * 解释一个给定的表达式
    *
    * @param context
    */
   @Override
   public void interpret(Context context) {

       this.context = context;
       String input = context.getInput();
       int in = Integer.valueOf(input);
       context.setOutput(in+1);

   }

   @Override
   public String toString() {
       return "++"+context.getInput()+"="+context.getOutput();
   }
}

public class Client {

   public static void main(String[] args) {

       Context context = new Context("50");
       
       Expression plus = new PlusExpression();
       Expression minus = new MinusExpression();
       //执行垒加
       plus.interpret(context);
       System.out.println(plus.toString());
       //垒减
       minus.interpret(context);
       System.out.println(minus.toString());

   }

}
  • 命令模式
  • 有些时候我们想某个对象发送一个请求,但是我们并不知道该请求的具体接收者是谁,具体的处理过程是如何的,们只知道在程序运行中指定具体的请求接收者即可,对于这样将请求封装成对象的我们称之为命令模式。所以命令模式将请求封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。同时命令模式支持可撤销的操作。
  • 命令模式可以将请求的发送者和接收者之间实现完全的解耦,发送者和接收者之间没有直接的联系,发送者只需要知道如何发送请求命令即可,其余的可以一概不管,甚至命令是否成功都无需关心。同时我们可以非常方便的增加新的命令,但是可能就是因为方便和对请求的封装就会导致系统中会存在过多的具体命令类。
/**
* 烧烤师傅
*/
public class Barbecue {

   public void makeMutton() {
       System.out.println("烤羊肉串");
   }

   public void makeChicken() {
       System.out.println("考鸡肉串");
   }
}

/**
* 命令抽象父类
*/
public abstract class Commond {

   private Barbecue barbecue;

   public Commond(Barbecue barbecue) {
       super();
       this.barbecue = barbecue;
   }

   public abstract void excuteCommond();

   public Barbecue getBarbecue() {
       return barbecue;
   }

   public void setBarbecue(Barbecue barbecue) {
       this.barbecue = barbecue;
   }
}

/**
* 烤鸡翅命令
*/
public class ChickenCommond extends Commond{

   public ChickenCommond(Barbecue barbecue) {
       super(barbecue);
   }
   @Override
   public void excuteCommond() {
       super.getBarbecue().makeChicken();
   }

}

/**
* 烤羊腿命令
*/
public class MuttonCommod extends Commond{

   public MuttonCommod(Barbecue barbecue) {
       super(barbecue);
   }
   @Override
   public void excuteCommond() {
       super.getBarbecue().makeMutton();
   }

}

/**
* 服务员
*/
public class Waiter {

   private List<Commond> commonds = new ArrayList<>();

   public void addCommond(Commond commond) {
       // TODO 可以做很多事情  记日志等等
       commonds.add(commond);
   }

   public void removeCommond(Commond commond) {
       // TODO 可以做很多事情  记日志等等
       commonds.remove(commond);
   }

   public void Notify() {
       for (Commond commond : commonds) {
           commond.excuteCommond();
       }
   }
}

/**
* 客户端
*/
public class Test {

   public static void main(String[] args) {
       Barbecue barbecue = new Barbecue();
       Commond commond = new ChickenCommond(barbecue);
       Waiter waiter = new Waiter();
       waiter.addCommond(commond);
       waiter.Notify();
   }
}
  • 责任链模式
  • 职责链模式描述的请求如何沿着对象所组成的链来传递的。它将对象组成一条链,发送者将请求发给链的第一个接收者,并且沿着这条链传递,直到有一个对象来处理它或者直到最后也没有对象处理而留在链末尾端。
  • 避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止,这就是职责链模式。在职责链模式中,使得每一个对象都有可能来处理请求,从而实现了请求的发送者和接收者之间的解耦。同时职责链模式简化了对象的结构,它使得每个对象都只需要引用它的后继者即可,而不必了解整条链,这样既提高了系统的灵活性也使得增加新的请求处理类也比较方便。但是在职责链中我们不能保证所有的请求都能够被处理,而且不利于观察运行时特征。
package laona;

/**
* 模拟请求对象
*/
public class Request {
   // 简单封装一种字符串类型
   private String requestString;

   public Request (String message) {
       this.requestString = message;
   }

   public String getRequestString() {
       return requestString;
   }

   public void setRequestString(String requestString) {
       this.requestString = requestString;
   }
}

package laona;

/**
* 模拟响应对象
*/
public class Response {

   private String responseString;

   public Response (String message) {
       this.responseString = message;
   }

   public String getResponseString() {
       return responseString;
   }

   public void setResponseString(String responseString) {
       this.responseString = responseString;
   }
}

package laona;

/**
* 责任链模式  请求正序  响应倒序 通过过滤器
*/
public interface Filter {

   /**
    * doFilter();
    * 即过滤 request 又要过滤 response
    * @param request
    * @param response
    * @return
    */
   void doFilter(Request request, Response response, FilterChain chain);

}

package laona;

import java.util.ArrayList;
import java.util.List;

public class FilterChain implements Filter {

   private Integer index = 0;

   /**
    * 定义一个list集合用于存取Filter
    */
   private List<Filter> filterList = new ArrayList<>();

   /**
    * 向集合中添加数据
    * @param filter
    * @return
    */
   public FilterChain addFilter (Filter filter) {
       filterList.add(filter);
       return this;
   }

   /**
    * 从集合中添加元素
    * @param filter
    * @return
    */
   public FilterChain deleteFilter (Filter filter) {

       filterList.remove(filter);
       return this;
   }
   public List<Filter> getFilterList() {
       return filterList;
   }
   /**
    * 实现接口Filter中的doFilter方法
    * @param request
    * @param response
    */
   @Override
   public void doFilter(Request request, Response response, FilterChain chain) {

       // 没有filter
       if (index == filterList.size()) {
           return;
       }
       Filter filter = filterList.get(index);
       index ++;

       filter.doFilter(request, response, chain);
   }
}

package laona;

/**
* html 自定义过滤器
*/
public class HTMLFilter implements Filter {


   /**
    * 将<html>转换成[html]
    * @param request
    * @param response
    */
   @Override
   public void doFilter (Request request, Response response, FilterChain chain) {

       // 处理request
       request.getRequestString().replace("<", "{")
               .replace(">","]" );

       System.out.println("---> request HTMLFilter <---");
       // 进入下一个filter
       chain.doFilter(request, response, chain);
       // 处理response
       response.getResponseString().replace("", "");
       System.out.println("---> response HTMLFilter <---");
   }
}

package laona;

/**
* 去除空格 过滤器
*/
public class NullFilter implements Filter {


   @Override
   public void doFilter (Request request, Response response, FilterChain chain) {

       // 处理request
       request.setRequestString(request.getRequestString().replaceAll(" ", ""));

       System.out.println("---> request NullFilter <---");

       chain.doFilter(request, response, chain);
       // 处理response
       response.getResponseString().replace("", "");
       System.out.println("---> response NullFilter <---");
   }
}

package laona;

/**
* BadLanguageFilter 过滤器
*/
public class BadLanguageFilter implements Filter {

   /**
    * 将和尚等字符转换成*
    * @param request
    * @param response
    */
   @Override
   public void doFilter (Request request, Response response, FilterChain chain) {

       // 处理request
       request.setRequestString(request.getRequestString().replace("和尚", "*"));

       System.out.println("---> reques BadLanguageFilter <---"); chain.doFilter(request, response, chain);

     // 处理response 
    response.getResponseString().replace("", ""); System.out.println("---> response BadLanguageFilter <---"); } }

package laona;

import org.junit.Test;

/**
* 责任链模式 测试类
*/
public class ResponsibilityTest {

 @Test
   public void responsibilityTest01 () {

       String message = "woshi 老衲,<html>,你是哪个和尚?";

       FilterChain filterChain = new FilterChain();

       Request request = new Request(message);
       Response response = new Response(message);

       filterChain.addFilter(new NullFilter()).addFilter(new BadLanguageFilter()).addFilter(new HTMLFilter());

       filterChain.doFilter(request, response, filterChain);

       System.out.println(request.getRequestString());
       System.out.println(response.getResponseString());
   }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,875评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,569评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,475评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,459评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,537评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,563评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,580评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,326评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,773评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,086评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,252评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,921评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,566评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,190评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,435评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,129评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,125评论 2 352