首页 文章 精选 留言 我的

精选列表

搜索[网站开发],共10000篇文章
优秀的个人博客,低调大师

Google 推出免费利器,让编程小白也能开发游戏

雷锋网消息,6 月 13 日,Google 在一篇博客中表示,使用 Google 原型游戏生成器 Game Builder,用户无需具备代码知识,就能通过一些列卡牌来创建属于自己的 3D 游戏。 这就意味着,创建游戏不再是程序员的专利,普通游戏玩家也可以随心所欲地创造自己的游戏王国。 创建自己的游戏 Game Builder 的设计旨在边玩边建,让玩家在创建游戏的同时,又能享受到玩游戏的快乐。比如,玩家建立起台阶之后,游戏里的人物会爬上台阶完成任务。 多人协同游戏/创建 玩家创建的个人游戏并不是一个完全封锁的世界,他还可以邀请朋友们一起来玩自己设计的游戏,甚至邀请朋友们参与游戏的建设工作,享受多人协同的过程。 卡牌代替编码知识 Game Builder 里面还会提供海量可视卡牌,当系统向你提问要怎样建设游戏时,你可以拖动卡牌来回答问题,比如,“您希望爆炸的样式是什么?”,你可以拖动自己喜欢的爆炸样式,然后将卡牌拖动到相应的回答框。这样一来,图片就代替了代码。 成千上万的 3D 模型 除了海量卡牌,Game Builder 还提供了成千上万的 3D 模型来帮助玩家丰富游戏内容,比如汽车、飞机、甚至宠物。 实时 JavaScript 编程 用于技术专长的玩家可以选择使用 JavaScript,并调整现有的应用程序编程接口(API),从而编写游戏里的内容。而且,所有代码都是实时的,所以只需要进行更改并保存,无需编译。 需要说明的是,用户要想使用 Google Game Builder,需要通过 PC 和 Mac 上的 Steam 平台里来找到 ,而且是完全免费的。 雷锋网(公众号:雷锋网)注:本文图片来自 Google Blog 雷锋网版权文章,未经授权禁止转载。详情见转载须知。

优秀的个人博客,低调大师

开发中滥用面向对象,你是否违背了编程原则

Switch 声明 Switch 声明(Switch Statements) 你有一个复杂的switch语句或if序列语句。 问题原因 面向对象程序的一个最明显特征就是:少用switch和case语句。从本质上说,switch语句的问题在于重复(if序列也同样如此)。你常会发现switch语句散布于不同地点。如果要为它添加一个新的case子句,就必须找到所有switch语句并修改它们。面向对象中的多态概念可为此带来优雅的解决办法。 大多数时候,一看到switch语句,就应该考虑以多态来替换它。 解决方法 问题是多态该出现在哪?switch 语句常常根据类型码进行选择,你要的是“与该类型码相关的函数或类”,所以应该运用提炼函数(Extract Method)将switch语句提炼到一个独立函数中,再以搬移函数(Move Method)将它搬移到需要多态性的那个类里。 如果你的switch是基于类型码来识别分支,这时可以运用以子类取代类型码(Replace Type Code with Subclass)或以状态/策略模式取代类型码(Replace Type Code with State/Strategy)。 一旦完成这样的继承结构后,就可以运用以多态取代条件表达式(Replace Conditional with Polymorphism)了。 如果条件分支并不多并且它们使用不同参数调用相同的函数,多态就没必要了。在这种情况下,你可以运用以明确函数取代参数(Replace Parameter with Explicit Methods)。 如果你的选择条件之一是 null,可以运用引入 Null 对象(Introduce Null Object)。 收益 提升代码组织性。 何时忽略 如果一个switch操作只是执行简单的行为,就没有重构的必要了。 switch常被工厂设计模式族(工厂方法模式(Factory Method)和抽象工厂模式(Abstract Factory))所使用,这种情况下也没必要重构。 重构方法说明 提炼函数(Extract Method) 问题 你有一段代码可以组织在一起。 void printOwing() { printBanner(); //print details System.out.println("name: " + name); System.out.println("amount: " + getOutstanding()); } 解决 移动这段代码到一个新的函数中,使用函数的调用来替代老代码。 void printOwing() { printBanner(); printDetails(getOutstanding()); } void printDetails(double outstanding) { System.out.println("name: " + name); System.out.println("amount: " + outstanding); } 搬移函数(Move Method) 问题 你的程序中,有个函数与其所驻类之外的另一个类进行更多交流:调用后者,或被后者调用。 解决 在该函数最常引用的类中建立一个有着类似行为的新函数。将旧函数变成一个单纯的委托函数,或是旧函数完全移除。 以子类取代类型码(Replace Type Code with Subclass) 问题 你有一个不可变的类型码,它会影响类的行为。 解决 以子类取代这个类型码。 以状态/策略模式取代类型码(Replace Type Code with State/Strategy) 问题 你有一个类型码,它会影响类的行为,但你无法通过继承消除它。 解决 以状态对象取代类型码。 以多态取代条件表达式(Replace Conditional with Polymorphism) 问题 你手上有个条件表达式,它根据对象类型的不同而选择不同的行为。 class Bird { //... double getSpeed() { switch (type) { case EUROPEAN: return getBaseSpeed(); case AFRICAN: return getBaseSpeed() - getLoadFactor() * numberOfCoconuts; case NORWEGIAN_BLUE: return (isNailed) ? 0 : getBaseSpeed(voltage); } throw new RuntimeException("Should be unreachable"); } } 解决 将这个条件表达式的每个分支放进一个子类内的覆写函数中,然后将原始函数声明为抽象函数。 abstract class Bird { //... abstract double getSpeed(); } class European extends Bird { double getSpeed() { return getBaseSpeed(); } } class African extends Bird { double getSpeed() { return getBaseSpeed() - getLoadFactor() * numberOfCoconuts; } } class NorwegianBlue extends Bird { double getSpeed() { return (isNailed) ? 0 : getBaseSpeed(voltage); } } // Somewhere in client code speed = bird.getSpeed(); 以明确函数取代参数(Replace Parameter with Explicit Methods) 问题 你有一个函数,其中完全取决于参数值而采取不同的行为。 void setValue(String name, int value) { if (name.equals("height")) { height = value; return; } if (name.equals("width")) { width = value; return; } Assert.shouldNeverReachHere(); } 解决 针对该参数的每一个可能值,建立一个独立函数。 void setHeight(int arg) { height = arg; } void setWidth(int arg) { width = arg; } 引入 Null 对象(Introduce Null Object) 问题 你需要再三检查某对象是否为 null。 if (customer == null) { plan = BillingPlan.basic(); } else { plan = customer.getPlan(); } 解决 将 null 值替换为 null 对象。 class NullCustomer extends Customer { Plan getPlan() { return new NullPlan(); } // Some other NULL functionality. } // Replace null values with Null-object. customer = (order.customer != null) ? order.customer : new NullCustomer(); // Use Null-object as if it's normal subclass. plan = customer.getPlan(); 临时字段 临时字段(Temporary Field)的值只在特定环境下有意义,离开这个环境,它们就什么也不是了。 问题原因 有时你会看到这样的对象:其内某个实例变量仅为某种特定情况而设。这样的代码让人不易理解,因为你通常认为对象在所有时候都需要它的所有变量。在变量未被使用的情况下猜测当初设置目的,会让你发疯。 通常,临时字段是在某一算法需要大量输入时而创建。因此,为了避免函数有过多参数,程序员决定在类中创建这些数据的临时字段。这些临时字段仅仅在算法中使用,其他时候却毫无用处。 这种代码不好理解。你期望查看对象字段的数据,但是出于某种原因,它们总是为空。 解决方法 可以通过提炼类(Extract Class)将临时字段和操作它们的所有代码提炼到一个单独的类中。此外,你可以运用以函数对象取代函数(Replace Method with Method Object)来实现同样的目的。 引入 Null 对象(Introduce Null Object)在“变量不合法”的情况下创建一个 null 对象,从而避免写出条件表达式。 收益 更好的代码清晰度和组织性。 重构方法说明 提炼类(Extract Class) 问题 某个类做了不止一件事。 解决 建立一个新类,将相关的字段和函数从旧类搬移到新类。 以函数对象取代函数(Replace Method with Method Object) 问题 你有一个过长函数,它的局部变量交织在一起,以致于你无法应用提炼函数(Extract Method) 。 class Order { //... public double price() { double primaryBasePrice; double secondaryBasePrice; double tertiaryBasePrice; // long computation. //... } } 解决 将函数移到一个独立的类中,使得局部变量成了这个类的字段。然后,你可以将函数分割成这个类中的多个函数。 class Order { //... public double price() { return new PriceCalculator(this).compute(); } } class PriceCalculator { private double primaryBasePrice; private double secondaryBasePrice; private double tertiaryBasePrice; public PriceCalculator(Order order) { // copy relevant information from order object. //... } public double compute() { // long computation. //... } } 引入 Null 对象(Introduce Null Object) 问题 你需要再三检查某对象是否为 null。 if (customer == null) { plan = BillingPlan.basic(); } else { plan = customer.getPlan(); } 解决 将 null 值替换为 null 对象。 class NullCustomer extends Customer { Plan getPlan() { return new NullPlan(); } // Some other NULL functionality. } // Replace null values with Null-object. customer = (order.customer != null) ? order.customer : new NullCustomer(); // Use Null-object as if it's normal subclass. plan = customer.getPlan(); 异曲同工的类 异曲同工的类(Alternative Classes with Different Interfaces) 两个类中有着不同的函数,却在做着同一件事。 问题原因 这种情况往往是因为:创建这个类的程序员并不知道已经有实现这个功能的类存在了。 解决方法 如果两个函数做同一件事,却有着不同的签名,请运用函数改名(Rename Method)根据它们的用途重新命名。 运用搬移函数(Move Method)、添加参数(Add Parameter)和令函数携带参数(Parameterize Method)来使得方法的名称和实现一致。 如果两个类仅有部分功能是重复的,尝试运用提炼超类(Extract Superclass)。这种情况下,已存在的类就成了超类。 当最终选择并运用某种方法来重构后,也许你就能删除其中一个类了。 收益 消除了不必要的重复代码,为代码瘦身了。 代码更易读(不再需要猜测为什么要有两个功能相同的类)。 何时忽略 有时合并类是不可能的,或者是如此困难以至于没有意义。例如:两个功能相似的类存在于不同的 lib 库中。 重构方法说明 函数改名(Rename Method) 问题 函数的名称未能恰当的揭示函数的用途。 class Person { public String getsnm(); } 解决 修改函数名。 class Person { public String getSecondName(); } 搬移函数(Move Method) 问题 你的程序中,有个函数与其所驻类之外的另一个类进行更多交流:调用后者,或被后者调用。 解决 在该函数最常引用的类中建立一个有着类似行为的新函数。将旧函数变成一个单纯的委托函数,或是旧函数完全移除。 添加参数(Add Parameter) 问题某个函数需要从调用端得到更多信息。 class Customer { public Contact getContact(); } 解决为此函数添加一个对象函数,让改对象带进函数所需信息。 class Customer { public Contact getContact(Date date); } 令函数携带参数(Parameterize Method) 问题 若干函数做了类似的工作,但在函数本体中却包含了不同的值。 解决 建立单一函数,以参数表达哪些不同的值。 提炼超类(Extract Superclass) 问题 两个类有相似特性。 解决 为这两个类建立一个超类,将相同特性移至超类。 被拒绝的馈赠 被拒绝的馈赠(Refused Bequest) 子类仅仅使用父类中的部分方法和属性。其他来自父类的馈赠成为了累赘。 问题原因 有些人仅仅是想重用超类中的部分代码而创建了子类。但实际上超类和子类完全不同。 解决方法 如果继承没有意义并且子类和父类之间确实没有共同点,可以运用以委托取代继承(Replace Inheritance with Delegation)消除继承。 如果继承是适当的,则去除子类中不需要的字段和方法。运用提炼超类(Extract Superclass)将所有超类中对于子类有用的字段和函数提取出来,置入一个新的超类中,然后让两个类都继承自它。 收益 提高代码的清晰度和组织性。 重构方法说明 以委托取代继承(Replace Inheritance with Delegation) 问题 某个子类只使用超类接口中的一部分,或是根本不需要继承而来的数据。 解决 在子类中新建一个字段用以保存超类; 调整子类函数,令它改而委托超类; 然后去掉两者之间的继承关系。 提炼超类(Extract Superclass) 问题 两个类有相似特性。 解决 为这两个类建立一个超类,将相同特性移至超类。

优秀的个人博客,低调大师

大数据开发:剖析Hadoop和Spark的Shuffle过程差异

一、前言 对于基于MapReduce编程范式的分布式计算来说,本质上而言,就是在计算数据的交、并、差、聚合、排序等过程。而分布式计算分而治之的思想,让每个节点只计算部分数据,也就是只处理一个分片,那么要想求得某个key对应的全量数据,那就必须把相同key的数据汇集到同一个Reduce任务节点来处理,那么Mapreduce范式定义了一个叫做Shuffle的过程来实现这个效果。 二、编写本文的目的 本文旨在剖析Hadoop和Spark的Shuffle过程,并对比两者Shuffle的差异。 三、Hadoop的Shuffle过程 Shuffle描述的是数据从Map端到Reduce端的过程,大数据学习kou群74零零加【41三八yi】大致分为排序(sort)、溢写(spill)、合并(merge)、拉取拷贝(Copy)、合并排序(merge sort)这几个过程,大体流程如下: ![image](https://yqfile.alicdn.com/e4ccedfb6ccaaa0d3c0ad5b3b7ab83d96dd9fed2.png) 上图的Map的输出的文件被分片为红绿蓝三个分片,这个分片的就是根据Key为条件来分片的,分片算法可以自己实现,例如Hash、Range等,最终Reduce任务只拉取对应颜色的数据来进行处理,就实现把相同的Key拉取到相同的Reduce节点处理的功能。下面分开来说Shuffle的的各个过程。 Map端做了下图所示的操作: 1、Map端sort Map端的输出数据,先写环形缓存区kvbuffer,当环形缓冲区到达一个阀值(可以通过配置文件设置,默认80),便要开始溢写,但溢写之前会有一个sort操作,这个sort操作先把Kvbuffer中的数据按照partition值和key两个关键字来排序,移动的只是索引数据,排序结果是Kvmeta中数据按照partition为单位聚集在一起,同一partition内的按照key有序。 2、spill(溢写) 当排序完成,便开始把数据刷到磁盘,刷磁盘的过程以分区为单位,一个分区写完,写下一个分区,分区内数据有序,最终实际上会多次溢写,然后生成多个文件 3、merge(合并) spill会生成多个小文件,对于Reduce端拉取数据是相当低效的,那么这时候就有了merge的过程,合并的过程也是同分片的合并成一个片段(segment),最终所有的segment组装成一个最终文件,那么合并过程就完成了,如下图所示 至此,Map的操作就已经完成,Reduce端操作即将登场 Reduce操作 总体过程如下图的红框处: ![image](https://yqfile.alicdn.com/71a52ed4799d3dbbde4552028f3aea05bc1c98c0.png) 1、拉取拷贝(fetch copy) Reduce任务通过向各个Map任务拉取对应分片。这个过程都是以Http协议完成,每个Map节点都会启动一个常驻的HTTP server服务,Reduce节点会请求这个Http Server拉取数据,这个过程完全通过网络传输,所以是一个非常重量级的操作。 2、合并排序 Reduce端,拉取到各个Map节点对应分片的数据之后,会进行再次排序,排序完成,结果丢给Reduce函数进行计算。 四、总结 至此整个shuffle过程完成,最后总结几点: 1、shuffle过程就是为了对key进行全局聚合 2、排序操作伴随着整个shuffle过程,所以Hadoop的shuffle是sort-based的 Spark shuffle相对来说更简单,因为不要求全局有序,所以没有那么多排序合并的操作。Spark shuffle分为write和read两个过程。我们先来看shuffle write。 一、shuffle write shuffle write的处理逻辑会放到该ShuffleMapStage的最后(因为spark以shuffle发生与否来划分stage,也就是宽依赖),final RDD的每一条记录都会写到对应的分区缓存区bucket,如下图所示: 说明: 1、上图有2个CPU,可以同时运行两个ShuffleMapTask 2、每个task将写一个buket缓冲区,缓冲区的数量和reduce任务的数量相等 3、 每个buket缓冲区会生成一个对应ShuffleBlockFile 4、ShuffleMapTask 如何决定数据被写到哪个缓冲区呢?这个就是跟partition算法有关系,这个分区算法可以是hash的,也可以是range的 5、最终产生的ShuffleBlockFile会有多少呢?就是ShuffleMapTask 数量乘以reduce的数量,这个是非常巨大的 那么有没有办法解决生成文件过多的问题呢?有,开启FileConsolidation即可,开启FileConsolidation之后的shuffle过程如下: 在同一核CPU执行先后执行的ShuffleMapTask可以共用一个bucket缓冲区,然后写到同一份ShuffleFile里去,上图所示的ShuffleFile实际上是用多个ShuffleBlock构成,那么,那么每个worker最终生成的文件数量,变成了cpu核数乘以reduce任务的数量,大大缩减了文件量。 二、Shuffle read Shuffle write过程将数据分片写到对应的分片文件,这时候万事具备,只差去拉取对应的数据过来计算了。 那么Shuffle Read发送的时机是什么?是要等所有ShuffleMapTask执行完,再去fetch数据吗?理论上,只要有一个 ShuffleMapTask执行完,就可以开始fetch数据了,实际上,spark必须等到父stage执行完,才能执行子stage,所以,必须等到所有 ShuffleMapTask执行完毕,才去fetch数据。fetch过来的数据,先存入一个Buffer缓冲区,所以这里一次性fetch的FileSegment不能太大,当然如果fetch过来的数据大于每一个阀值,也是会spill到磁盘的。 fetch的过程过来一个buffer的数据,就可以开始聚合了,这里就遇到一个问题,每次fetch部分数据,怎么能实现全局聚合呢?以word count的reduceByKey(《Spark RDD操作之ReduceByKey 》)为例,假设单词hello有十个,但是一次fetch只拉取了2个,那么怎么全局聚合呢?Spark的做法是用HashMap,聚合操作实际上是map.put(key,map.get(key)+1),将map中的聚合过的数据get出来相加,然后put回去,等到所有数据fetch完,也就完成了全局聚合。 三、总结 Hadoop的MapReduce Shuffle和Spark Shuffle差别总结如下: 1、Hadoop的有一个Map完成,Reduce便可以去fetch数据了,不必等到所有Map任务完成,而Spark的必须等到父stage完成,也就是父stage的map操作全部完成才能去fetch数据。 2、Hadoop的Shuffle是sort-base的,那么不管是Map的输出,还是Reduce的输出,都是partion内有序的,而spark不要求这一点。 3、Hadoop的Reduce要等到fetch完全部数据,才将数据传入reduce函数进行聚合,而spark是一边fetch一边聚合。

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册