继续把我们的设计模式捡起,希望我能坚持完这个系列吧,下面我们就进入正题吧。
在软件开发过程中,我们需要重复使用某个对象的时候,如果重复地new这个对象,不停地申请内存空间,会造成内存空间的极大浪费,在之后程序运行过程中也可能会产生大量的垃圾对象,给服务器的垃圾回收带来极大压力,那么我们从软件设计的角度该如何解决这个问题呢?单例模式就可以解决这个问题了。在之前的单例模式中我们提到“单例模式提供了一个全局访问点,来访问其唯一的实例对象”,单例模式强调系统中有且仅有唯一的实例对象。
更进一步,假如系统中就是需要创建多个(并不是无限制)相同或者相似(也有可能相同)的对象,那我们该如何处理呢?比如数据库连接,使用的时候不可能每次都创建和销毁,当然也不能使用单例只创建一个连接,负责处理所有的客户端请求,我们可以使用数据连接池技术,创建一定数量的连接的缓存,使用的时直接拿出来使用就可以了,这种模式从创建对象的角度来看也算是“享元模式”的一种典型应用,下面我们就来学习一下该模式。
定义:享元模式(FlyWeight Pattern)主要用来减少创建对象的数量,以减少内存占用,达到提高性能目的,这种模式也属于结构型设计模式,享元模式尝试复用现有的同类对象,如果未找到匹配对象,则创建新对象,此模式是一种专门为提升系统性能而生的设计模式。
要理解享元模式,先来了解两个概念,内部状态和外部状态:
内部状态:在享元对象内部不随外界环境改变而改变的共享部分;
外部状态:随着环境的改变而改变,不能功能构想的状态就是外部状态;
享元模式区分了内部状态和外部状态,所以我们可以通过设置不同的外部状态使得相同的对象可以具备一些不同的特性,而内部状态则设置为相同的共享部分。
享元模式结构图:

角色分析:
1、Flyweight:抽象的享元角色,通常是一个接口或者抽象类,在抽象享元角色中声明了具体享元角色中的公共方法,这些方法可以向外界提供享元对象的内部数据(内部状态),同时也可以通过这些方法来设置外部数据(外部状态)
2、ConcreteFlyweight:具体享元角色,继承或实现Flyweight接口,称为享元对象,通常结合单例模式来设计具体享元类,为每一个享元类提供唯一的享元对象;
3、UnsharedConcreteFlyweight:指那些不需要共享的Flyweight子类,它并不强制共享;
4、FlyweightFactory:用来创建并管理Flyweight对象,主要用来确保合理第共享Flyweight,当用户请求一个Flyweight时,FlyweightFactory工厂提供一个已经创建的实例或者新创建一个(如果不存在的话);
举例分析:
例如我们小时经常俄罗斯方块游戏,它每次落下来的图形都不一定相同,假如我们每次都new一个图形的话那么会占用大量内存,体验并不好;其实玩久了我们会发现,它每次落下来的图形就那么 几种,包括“L”型、“M”型、“Z”型、“S”型、“I”型等有限的几种类型,那么我们就可以将这有限的几种类型抽象出来,用享元模式来实现,为了更好地说明享元模式,再高级一点我们给这些图形还带上颜色,下面我们就来具体分析吧:
示例代码:
package cn.com.pep.model.flyweight;
/**
*
* @Title: AbstaractBox
* @Description: Flyweight:抽象享元角色,声明了具体享元角色中的方法,向外界提供享元对象的内部状态,同时也可以通过这些方法来设置对象的外部状态
* @author wwh
* @date 2022-9-13 14:14:22
*/
public abstract class AbstaractBox{
/**
* @Title: getShape
* @Description: 向外界提供享元对象的内部状态,即形状。
* @return
*/
public abstract String getShape();
/**
* @Title: display
* @Description: 通过此方法来设置对象的外部状态
* @param color
*/
public void display(String color) {
System.err.println("本次落下来的图形是:" + this.getShape() + ",颜色是:" + color);
}
}
package cn.com.pep.model.flyweight;
/**
*
* @Title: IBox
* @Description: 具体享元角色,为每一个享元类提供唯一的实例
* @author wwh
* @date 2022-9-13 14:23:28
*/
public class IBox extends AbstaractBox{
@Override
public String getShape() {
return "IBox";
}
}
package cn.com.pep.model.flyweight;
/**
*
* @Title: MBox
* @Description: 具体享元角色,为每一个享元类提供唯一的实例
* @author wwh
* @date 2022-9-13 14:25:04
*/
public class MBox extends AbstaractBox{
@Override
public String getShape() {
return "MBox";
}
}
package cn.com.pep.model.flyweight;
/**
*
* @Title: ZBox
* @Description: 具体享元角色,为每一个享元类提供唯一的实例
* @author wwh
* @date 2022-9-13 14:25:52
*/
public class ZBox extends AbstaractBox{
@Override
public String getShape() {
return "ZBox";
}
}
package cn.com.pep.model.flyweight;
import java.util.HashMap;
/**
*
* @Title: BoxFactory
* @Description:享元工厂,用来创建并管理Flyweight对象,当用户请求一个Flyweight对象时,FlyweightFactory工厂提供一个已经创建的实例或者新创建一个实例;
* @author wwh
* @date 2022-9-13 14:26:27
*/
public class BoxFactory {
/**
* 创建一个池,用来缓存需要共享的享元对象
*/
private static HashMap<String, AbstaractBox> map = new HashMap<>();
public BoxFactory() {
map.put("I", new IBox());
map.put("M", new MBox());
map.put("Z", new ZBox());
}
private static class SingtonHolder{
private static final BoxFactory INSTANCE = new BoxFactory();
}
/**
* @Title: getFactory
* @Description:
* @return
*/
public static final BoxFactory getFactory() {
return SingtonHolder.INSTANCE;
}
/**
* @Title: getBox
* @Description:
* @param box
* @return
*/
public AbstaractBox getBox(String box) {
if (map.containsKey(box)) {
return map.get(box);
}
return null;
}
}
package cn.com.pep.model.flyweight;
/**
*
* @Title: FlyweightPatternDemo
* @Description: 测试类
* @author wwh
* @date 2022-9-13 14:36:49
*/
public class FlyweightPatternDemo {
public static void main(String[] args) {
BoxFactory factory = BoxFactory.getFactory();
AbstaractBox box = factory.getBox("I");
box.display("红色");//传入外部状态--颜色
box.display("白色");
System.err.println(box);//打印“内部状态”
factory.getBox("I");
System.err.println(box);//再次打印“内部状态”
}
}
测试结果:

UML类图:

在上面这个例子中,图形的形状就是内部状态,而颜色我们就可以认为是外部状态。外部状态是相互独立的,而且不影响内部状态。
享元模式的优缺点和使用场景:
优点:极大地减少了内存中相似或者相同对象的数量,节约系统资源、提高系统性能;外部状态相互独立,不影响内部状态;
缺点:为了使对象可以共享,需要分离外部状态和内部状态,是程序逻辑复杂;
使用场景:
1、一个系统中有大量相同或者相似的对象,造成内存的大量耗费;
2、对象的大部分状态都可以外部化,可以将这些外部状态传入到对象中;
享元模式和单例模式比较:
单例模式和享元模式都可以减少系统中对象的创建数量,但是两者还有一些区别,主要包括以下方面的内容:
1、享元模式可以再次创建对象,也可以获取缓存的对象,单例模式严格控制单个进程中只有一个实例对象;
2、享元模式可以通过享元工厂实现对外部的单例,也可以在需要的时候创建更多的实例,单例模式是自身控制,需要增加不属于改对象本身的逻辑;
3、两者都可以实现节省对象的创建;
在JDK中的应用:
ThreadPool线程池、第三方提供的数据库连接池、JDK中的字符串常量池等都使用了享元模式、Integer中也有类似的代码;
public static Integer valueOf(int i) {
if (!$assertionsDisabled && IntegerCache.high < 127)
throw new AssertionError();
if (i >= -128 && i <= IntegerCache.high)
return IntegerCache.cache[i + 128];
else
return new Integer(i);
}
从这个例子我们可以看出当i>= -128 && i<=127的时候直接取缓冲池中缓存的对象,否则就直接new一个Integer对象返回。好了,本期也到了和大家说拜拜的时候了,小弟水平有限,还请各位大佬批评指正,共同进步!
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende
我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数
给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最
运行bundleinstall后出现此错误:Gem::Package::FormatError:nometadatafoundin/Users/jeanosorio/.rvm/gems/ruby-1.9.3-p286/cache/libv8-3.11.8.13-x86_64-darwin-12.gemAnerroroccurredwhileinstallinglibv8(3.11.8.13),andBundlercannotcontinue.Makesurethat`geminstalllibv8-v'3.11.8.13'`succeedsbeforebundling.我试试gemin
目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称
项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU
了解Rails缓存如何工作的人可以真正帮助我。这是嵌套在Rails::Initializer.runblock中的代码:config.after_initializedoSomeClass.const_set'SOME_CONST','SOME_VAL'end现在,如果我运行script/server并发出请求,一切都很好。然而,在我的Rails应用程序的第二个请求中,一切都因单元化常量错误而变得糟糕。在生产模式下,我可以成功发出第二个请求,这意味着常量仍然存在。我已通过将以上内容更改为以下内容来解决问题:config.after_initializedorequire'some_cl
我在我的项目中有一个用户和一个管理员角色。我使用Devise创建了身份验证。在我的管理员角色中,我没有任何确认。在我的用户模型中,我有以下内容:devise:database_authenticatable,:confirmable,:recoverable,:rememberable,:trackable,:validatable,:timeoutable,:registerable#Setupaccessible(orprotected)attributesforyourmodelattr_accessible:email,:username,:prename,:surname,: