设计模式
模式之间的关系如下:
原文出外
创建型模式
方法 | 解决的场景 |
工厂方法模式 | 实现了同一接口的一些类进行实例的创建 |
抽象工厂模式 | |
单例模式 | |
建造者模式 | |
原型模式 |
工厂方法模式分类
名称 | 分类 | 功能 |
工厂方法模式 | 普通工厂 | 缺点:1、生成对象时,也容易错, 2、需要创建Factory 3、每次有一个新的类型引入时,需要重新修改SendFactory类, |
多方法工厂 | 缺点:1、每次有一个新的类型引入时,需要重新修改SendFactory类, 2、需要创建Factory | |
静态方法工厂 |
1、简单工厂模式
实现代码
//Sender.java
public interface Sender{
void send();
}
//MailSender.java
public class MailSender implements Sender{
@Override
public void send(){
System.out.println("this is MailSender function");
}
}
//SmsSender.java
public class SmsSender implements Sender{
@override
public void send(){
System.out.println("this is SmsSender function");
}
//SendFactory
public class SendFactory{
public Sender produce(String senderName){
if("mailsend".equalsIgnoreCase(senderName){
return new MailSender();
}else if("smssend".equalsIgnoreCase(senderName){
return new SmsSender();
}else{
System.out.println("please input correct name");
return null;
}
}
}
//MyMain.java
public class MyMain{
public static void main(String[] args){
SendFactory factory = new SendFactory();
Sender sender = factory.produce("mailSend");
sender.send();
}
}
多方法工厂
代码如下:
//SendFactory.java
public class SendFactory{
public Send produceMail(){
return new MailSender();
}
public Send produceSms(){
return new SmsSender();
}
}
//MyMain.java
public class MyMain{
public void main(String[] args){
SendFactory factory = new SendFactory();
Sender sender = factory.produceSms();
sender.send()
}
}
静态方法工厂
//SendFactory.java
public class SendFactory{
public static Send produceMail(){
return new MailSender();
}
public static Send produceSms(){
return new SmsSender();
}
}
//MyMain.java
public class MyMain{
public void main(String[] args){
Sender sender = SendFactory.produceSms();
sender.send()
}
}
2、工厂方法模式(Factory Method)
简单工厂模式有一个问题就是,类的创建依赖工厂类,如果想要拓展程序,必须对工厂类进行修改,这违背了闭包原则,就用到工厂方法模式,创建一个工厂接口和创建多个工厂实现类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。
如下方式,解决了静态方法工厂类,每次新添加一个类时,就需要修改工厂方法类的问题。
//Provider.java
public interface Provider{
Sender produce();
}
//Sender.java
public interface Sender{
public void send();
}
//MailSender.java
public class MailSender implements Sender{
@Override
public void send(){
System.out.println("this is mailSender");
}
}
//SmsSender.java
public class SmsSender implements Sender{
@Override
public void send(){
System.out.println("this is SmsSender");
}
}
//SmsSendFactory.java
public class SmsSendFactory implements Provider{
@Override
public Sender produce(){
return SmsSender();
}
}
//MailSendFactory.java
public class MailSendFactory implements Provider{
@Override
public Sender produce(){
return MainSender();
}
}
public class MyMain{
public static void main(String[] args){
Provider provider1 = new SmsFactory();
Provider provider2 = new MailFactory();
Sender sender1 = provider1.produce();
Sender sender2 = provider2.produce();
sender1.send();
sender2.send();
}
}
3、抽象工厂模式(abstract method)
方法 | |
工厂方法模式 | |
抽象工厂模式 | |
区别:
工厂方法模式只有一个抽象产品类,而抽象工厂模式有多个。
工厂方法模式的具体工厂类只能创建一个具体产品类的实例,而抽象工厂模式可以创建多个。
工厂方法创建 "一种" 产品,他的着重点在于"怎么创建",也就是说如果你开发,你的大量代码很可能围绕着这种产品的构造,初始化这些细节上面。也因为如此,类似的产品之间有很多可以复用的特征,所以会和模版方法相随。
抽象工厂需要创建一些列产品,着重点在于"创建哪些"产品上,也就是说,如果你开发,你的主要任务是划分不同差异的产品线,并且尽量保持每条产品线接口一致,从而可以从同一个抽象工厂继承。
//Provider.class
public interface Provider{
Factory produce();
}
pulic interface KeyBoard{
void myName();
}
public interface Mouse{
void myName();
}
public class LGKeyBoard implements KeyBoard{
@Override
public void myName(){
System.out.println("I'am LG KeyBoard");
}
}
public class WRKeyBoard implements KeyBoard{
@Override
public void myName(){
System.out.println("I'am WR KeyBoard");
}
}
public class LGMouse implements Mouse{
@Override
public void myName(){
System.out.println("I'am WR Mouse");
}
}
public class WRMouse implements Mouse{
@Override
public void myName(){
System.out.println("I'am WR Mouse");
}
}
public class LGFactory implements Provider{
private Mouse mouse;
private KeyBoard keyBoard;
public void produce(){
this.mouse = new LGMouse();
this.keyBoard = new LGKeyBoard();
}
}
public class WRFactory implements Provider{
private Mouse mouse;
private KeyBoard keyBoard;
public void produce(){
this.mouse = new WRMouse();
this.keyBoard = new WRKeyBoard();
}
}
public class MyMain{
public static void main(String[] args){
Provider lg = new LGFactory();
Provider wr = new WRFactory();
}
}
4、单例模式(Singleton)
在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在
实现单例的步骤:
一、私有化构造函数
二、在类中实例化类
三、通过public方法返回实例化对象
public class Singleton{
private static Singleton instance = null;
private Singleton(){
}
public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
以上单例有如下问题:
1、在多线程时,无法保证只创建一个实例
2、实例只在调用时才被创建
public class Singleton{
private static Singleton instance = new Singleton();
private Singleton(){
}
public static Singleton getInstance(){
return instance;
}
}
采用了匿名内部类:
外部类加载时并不需要立即加载内部类,内部类不被加载则不去初始化INSTANCE,
故而不占内存。即当SingleTon第一次被加载时,并不需要
去加载SingletonInner,只有当getInstance()方法第一次被调用时,才会去初始化INSTANCE,
第一次调用getInstance()方法会导致虚拟机加载SingletonInner类,这种方法不仅能确保线程
安全,也能保证单例的唯一性,同时也延迟了单例的实例化。
原文链接:https://blog.csdn.net/mnb65482/article/details/80458571
public class Singleton{
private Singleton(){
}
private static class SingletonInner{
private static SingletonInner instance = new Singleton();
}
public SingletonInner getInstance(){
return SingletonInner.instance;
}
}
以下类出处https://www.cnblogs.com/leiqiannian/p/7922824.html
类中属性块方法加载顺序
一般顺序:静态块(静态变量)——>成员变量——>构造方法——>静态方法
public class test { //1.第一步,准备加载类
public static void main(String[] args) {
new test(); //4.第四步,new一个类,但在new之前要处理匿名代码块
}
static int num = 4; //2.第二步,静态变量和静态代码块的加载顺序由编写先后决定
{
num += 3;
System.out.println("b"); //5.第五步,按照顺序加载匿名代码块,代码块中有打印
}
int a = 5; //6.第六步,按照顺序加载变量
{ // 成员变量第三个
System.out.println("c"); //7.第七步,按照顺序打印c
}
test() { // 类的构造函数,第四个加载
System.out.println("d"); //8.第八步,最后加载构造函数,完成对象的建立
}
static { // 3.第三步,静态块,然后执行静态代码块,因为有输出,故打印a
System.out.println("a");
}
static void run() // 静态方法,调用的时候才加载// 注意看,e没有加载
{
System.out.println("e");
}
}
类加载时:JAVA虚拟机在有且仅有的5种场景下会对类进行初始化。
行动 | 场景 |
创建类的实例 | 实例化对象,读取或设置一个静态字段时,调用一个类的静态方法 |
访问某个类或者接口的静态变量,或者对该静态变量赋值(如果访问静态编译时常量(即编译时可以确定值的常量)不会导致类的初始化 | 如果类没进行初始化,需要先调用其初始化方法进行初始化 |
调用类的静态方法 | |
反射(Class.forName(xxx.xxx.xxx)) | |
初始化一个类的子类(相当于对父类的主动使用),不过直接通过子类引用父类元素,不会引起子类的初始化 | |
Java虚拟机被标明为启动类的类(包含main方法的) |
4、建造者模式(Builder)
一个复杂对象的构建与他的表示分离,使用者可以一步一步的构建一个比较复杂的对象。
参数的对象时有三种方式:构造器重载,JavaBeans模式和builder模式
构造器重载方式
public class Product {
private String id;
private String name;
private int age;
public Product() {
}
public Product(String id) {
this.id = id;
}
public Product(String id, String name) {
this.id = id;
this.name = name;
}
public Product(String id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
}
JavaBeans方式
public class Product {
private String id;
private String name;
private int age;
public Product() {
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
builder模式
public class ProductBuilder {
private final String id;
private final String name;
private final int age;
ProductBuilder(Builder builder){
this.id = builder.id;
this.name = builder.name;
this.age = builder.age;
}
public static class Builder{
private String id;
private String name;
private int age;
Builder id(String id){
this.id = id;
return this;
}
Builder name(String name){
this.name = name;
return this;
}
Builder age(int age){
this.age = age;
return this;
}
public ProductBuilder build(){
return new ProductBuilder(this);
}
}
}
ProductBuilder p3 = new ProductBuilder.Builder()
.id("10")
.name("phone")
.age(10)
.build();
5、原型模式(Prototype)
将一个对象作为原型,对其进行复制、克隆,产生一个和原对象类似的新对象
public class ProtoType implements Cloneable{
public Object clone() throw CloneNotSupportException{
ProtoType proto = (ProtoType)super.clone()
return proto;
}
}
浅复制:将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型,指向的还是原对象所指向的。
深复制:将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。
实现深复制的三种情况
情况一:
如果自定的对象的属性引用了基本数据类型,只要自定的对象实现Cloneable接口,并且重写clone()方法;
情况二:
如果自定的对象的属性引用了非基本数据类型,自定的对象与相应引用非基本数据类型的属性都要实现Cloneable接口,并且重写clone()方法;
情况三:
采用流的形式读入当前对象的二进制输入,再写出二进制数据对应的对象
情况一代码:
public class ProtoType implements Cloneable{
public Object clone()throw CloneNotSupportException{
ProtoType proto = (ProtoType)super.clone();
return proto;
}
}
情况二代码:
public class ProtoAType implements Cloneable{
public Object clone()throw CloneNotSupportException{
ProtoAType proto = (ProtoAType)super.clone();
return proto;
}
}
public class ProtoBType implements Cloneable{
public Object clone()throw CloneNotSupportException{
ProtoBType proto = (ProtoBType)super.clone();
return proto;
}
}
情况三代码:
public class ProtoCType{
public ProtoCType deepCopy(){
/* 写入当前对象的二进制流 */
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
/* 读出二进制流产生的新对象 */
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (ProtoCType)ois.readObject();
}
}