他们在学校里不会教你的编程原则

前言

在大学的时候,学校一般只会教你你写编程语言,比如C、C++、JAVA等编程语言。但是当你离开大学进入这个行业开始工作时,才知道编程不只是知道编程语言、语法等,要想写好代码,必须还要了解一些编程原则才行。本文主要讨论KISSDRYSOLID这些常见的编程原则,而且你会发现随着工作时间越久,越能感受这些编程原则的精妙之处,历久弥香。

KISS原则

Keep It Simple, Stupid!

你是不是有过接手同事的代码感到十分头疼的经历,明明可以有更加简单、明白的写法,非要绕来绕去,看不明白?

其实,我们在写代码的时候应该要遵守KISS原则,核心思想就是尽量保持简单。代码的可读性和可维护性是衡量代码质量非常重要的两个标准。而 KISS 原则就是保持代码可读和可维护的重要手段。代码足够简单,也就意味着很容易读懂,bug 比较难隐藏。即便出现 bug,修复起来也比较简单。

我们写代码的的时候要站在别人的角度出发,就像马丁·福勒说的,我们写的代码不是给机器看的,而是给人看的。

“任何傻瓜都可以编写计算机可以理解的代码。优秀的程序员编写出人类可以理解的代码。” — 马丁·福勒

那么如何才能写出满足KISS原则的代码呢?

如何写出KISS原则的代码?

我们直接上例子,下面的校验IP是否合法的3种实现方式,大家觉得哪个最KISS?

  1. 写法一

  1. 写法二

  1. 写法三

  • 写法一代码量最少,正则表达式本身是比较复杂的,写出完全没有 bug 的正则表达本身就比较有挑战;另一方面,并不是每个程序员都精通正则表达式。对于不怎么懂正则表达式的同事来说,看懂并且维护这段正则表达式是比较困难的。这种实现方式会导致代码的可读性和可维护性变差,所以,从 KISS 原则的设计初衷上来讲,这种实现方式并不符合 KISS 原则。
  • 写法二使用了 StringUtils 类、Integer 类提供的一些现成的工具函数,来处理 IP地址字符串,逻辑清晰,可读性好。
  • 写法三不使用任何工具函数,而是通过逐一处理 IP 地址中的字符,来判断是否合法,容易出bug,不好理解。

所以说,符合KISS原则的代码并不是代码越少越好,还要考虑代码是否逻辑清晰、是否容易理解、是否够稳定。

总结以下如何写出KISS原则的代码:

  1. 不要使用同事可能不懂的技术来实现代码。比如前面例子中的正则表达式,还有一些编程语言中过于高级的语法等。
  2. 不要重复造轮子,要善于使用已经有的工具类库。经验证明,自己去实现这些类库,出bug 的概率会更高,维护的成本也比较高。
  3. 不要过度优化。不要过度使用一些奇技淫巧(比如,位运算代替算术运算、复杂的条件语句代替 if-else、使用一些过于底层的函数等)来优化代码,牺牲代码的可读性。
  4. 主观站在别人的角度上编写代码。你在编写代码的时候就要思考我这个同事看这段代码是不是很快就能够明白理解。

DRY原则

Don't Repeat Yourself

你是不是有过这样的经历,项目中很多重复逻辑的代码,然后修改一个地方,另外一个地方忘记修改,导致测试给你提了很多bug?

DRY原则,英文全称Don’t Repeat Yourself,直译过来就是不要重复你自己。这里的重复不仅仅是代码一模一样,还包括实现逻辑重复、功能语义重复、代码执行重复等。我们不要偷懒,有责任把这些存在重复的地方识别出来,然后优化它们。

如何写出DRY原则的代码呢?

我们直接上例子,代码重复的我就不讲了,很好理解,关于实现逻辑或者功能语义重复的我觉个例子。

还是上面校验IP的例子,团队中两个同事由于不知道就有了两种写法。

  • 同事A写法

  • 同事B写法

尽管两段代码的实现逻辑不重复,但语义重复,也就是功能重复,我们认为它违反了 DRY 原则。我们应该在项目中,统一一种实现思路,所有用到判断 IP 地址是否合法的地方,都统一调用同一个函数。不然哪天校验规则变了,很容易只改了其中一个,另外一个漏改,就会出现莫名其妙的bug

其他的比如逻辑重复的意思是虽然功能是不一致的,但是里面的逻辑都是一模一样的。举个例子,比如校验用户名和校验密码,虽然功能不一致,但是校验逻辑都是相似,判空、字符长度等等,这种情况我们就需要把相似的逻辑抽取到一个方法中,不然也是不符合DRY原则。

那么我们平时写代码注意些什么才是符合DRY原则呢?

  • 使用现成的轮子,不轻易造轮子

其实最关键的就是写代码带脑子,用到一个方法先看看有没有现成的,不要看看不看,就动手在那里造轮子。

  • 减少代码耦合

对于高度耦合的代码,当我们希望复用其中的一个功能,想把这个功能的代码抽取出来成为一个独立的模块、类或者函数的时候,往往会发现牵一发而动全身。移动一点代码,就要牵连到很多其他相关的代码。所以,高度耦合的代码会影响到代码的复用性,我们要尽量减少代码耦合。

  • 满足单一职责原则

我们前面讲过,如果职责不够单一,模块、类设计得大而全,那依赖它的代码或者它依赖的代码就会比较多,进而增加了代码的耦合。根据上一点,也就会影响到代码的复用性。相反,越细粒度的代码,代码的通用性会越好,越容易被复用。

  • 模块化

这里的“模块”,不单单指一组类构成的模块,还可以理解为单个类、函数。我们要善于将功能独立的代码,封装成模块。独立的模块就像一块一块的积木,更加容易复用,可以直接拿来搭建更加复杂的系统。

  • 业务与非业务逻辑分离

越是跟业务无关的代码越是容易复用,越是针对特定业务的代码越难复用。所以,为了复用跟业务无关的代码,我们将业务和非业务逻辑代码分离,抽取成一些通用的框架、类库、组件等。

  • 通用代码下沉

从分层的角度来看,越底层的代码越通用、会被越多的模块调用,越应该设计得足够可复用。一般情况下,在代码分层之后,为了避免交叉调用导致调用关系混乱,我们只允许上层代码调用下层代码及同层代码之间的调用,杜绝下层代码调用上层代码。所以,通用的代码我们尽量下沉到更下层。

  • 继承、多态、抽象、封装

在讲面向对象特性的时候,我们讲到,利用继承,可以将公共的代码抽取到父类,子类复用父类的属性和方法。利用多态,我们可以动态地替换一段代码的部分逻辑,让这段代码可复用。除此之外,抽象和封装,从更加广义的层面、而非狭义的面向对象特性的层面来理解的话,越抽象、越不依赖具体的实现,越容易复用。代码封装成模块,隐藏可变的细节、暴露不变的接口,就越容易复用。

  • 应用模板等设计模式

一些设计模式,也能提高代码的复用性。比如,模板模式利用了多态来实现,可以灵活地替换其中的部分代码,整个流程模板代码可复用。

SOLID原则

SOLID原则不是一个单一的原则,而是对软件开发至关重要的 5 条原则,遵循这些原则有助于我们写出高内聚、低耦合、可扩展、可维护性好的代码。

S—单一职责原则

一个类应该有一个,而且只有一个改变它的理由。

单一职责原则在我看来是最容易理解也是最重要的一个原则。它的核心思想就是一个模块、类或者方法只做一件事,只有一个职责,千万不要越俎代庖。它可以带来下面的好处:

  • 可以让代码耦合度更低
  • 使代码更容易理解和维护
  • 使代码更易于测试和维护,使软件更易于实施,并有助于避免未来更改的意外副作用

举个例子,我们有两个类PersonAccount。 两者都负有存储其特定信息的单一责任。 如果要更改Person的状态,则无需修改类Account,反之亦然, 不要把账户的行为比如修改账户名changeAcctName写在Person类中。

    public class Person {
    	private Long personId;
    	private String firstName;
    	private String lastName;
    	private String age;
    	private List<Account> accounts;

        // 错误做法
        public void changeAcctName(Account account, String acctName) {
            acccount.setAccountName(acctName);
            // 更新到数据库
        }
    }

    public class Account {
    	private Long guid;
    	private String accountNumber;
    	private String accountName;
    	private String status;
    	private String type;

    }

所以大家在编写代码的时候,一定要停顿思考下这个段代码真的写在这里吗?另外很关键的一点是如果发现一个类或者一个方法十分庞大,那么很有可能已经违背单一职责原则了,后续维护可想而知十分痛苦。

O—开闭原则

软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。

对扩展开放,对修改关闭,什么意思?很简单,其实就是我们要尽量通过新增类实现功能,而不是修改原有的类或者逻辑。因为修改已有代码很有可能对已有功能引入bug。

让我们通过一个例子来理解这个原则,比如一个通知服务。

    public class NotificationService {
    	public void sendOTP(String medium) {
            if (medium.equals("email")) {
                //email 发送
            } else if (medium.equals("mobile")) {
                // 手机发送
        	} 
    }

现在需要新增微信的方式通知,你要怎么做呢? 是在加一个if else吗? 这样就不符合开闭原则了,我们看下开闭原则该怎么写。

  • 定义一个通知服务接口

    public interface NotificationService {
    	public void sendOTP();
    }
  • E-mail方式通知类EmailNotification

    public class EmailNotification implements NotificationService{
    	public void sendOTP(){
    		// write Logic using JavaEmail api
    	}
    }
  • 手机方式通知类MobileNotification

    public class MobileNotification implements NotificationService{
        public void sendOTP(){
    		// write Logic using Twilio SMS API
    	}
    }
  • 同样可以添加微信通知服务的实现WechatNotification

    public class WechatNotification implements NotificationService{
    	public void sendOTP(String medium){
    		// write Logic using wechat API
    	}
    }

这样的方式就是遵循开闭原则的,你不用修改核心的业务逻辑,这样可能带来意向不到的后果,而是扩展实现方式,由调用方根据他们的实际情况调用。

是不是想到了设计模式中的策略模式,其实设计模式就是指导我们写出高内聚、低耦合的代码。

L—里氏替换原则

派生类或子类必须可替代其基类或父类

这个原则稍微有点难以理解,它的核心思想是每个子类或派生类都应该可以替代/等效于它们的基类或父类。这样有一个好处,就是无论子类是什么类型,客户端通过父类调用都不会产生意外的后果。

理解不了?那我我们通过一个例子来理解一下。

让我们考虑一下我有一个名为 SocialMedia 的抽象类,它支持所有社交媒体活动供用户娱乐,如下所示:

    package com.alvin.solid.lsp;

    public abstract class SocialMedia {
        
        public abstract  void chatWithFriend();
        
        public abstract void publishPost(Object post);
        
        public abstract  void sendPhotosAndVideos();
        
        public abstract  void groupVideoCall(String... users);
    }

社交媒体可以有多个实现或可以有多个子类,如 FacebookWechatWeiboTwitter 等。

现在让我们假设 Facebook 想要使用这个特性或功能。

    package com.alvin.solid.lsp;

    public class Wechat extends SocialMedia {

        public void chatWithFriend() {
            //logic  
        }

        public void publishPost(Object post) {
            //logic  
        }

        public void sendPhotosAndVideos() {
            //logic  
        }

        public void groupVideoCall(String... users) {
            //logic  
        }
    }

我们都知道Facebook都提供了所有上述的功能,所以这里我们可以认为FacebookSocialMedia类的完全替代品,两者都可以无中断地替代。

现在让我们讨论 Weibo

    package com.alvin.solid.lsp;

    public class Weibo extends SocialMedia {
        public void chatWithFriend() {
            //logic
        }

        public void publishPost(Object post) {
          //logic
        }

        public void sendPhotosAndVideos() {
          //logic
        }

        public void groupVideoCall(String... users) {
            //不适用
        }
    }

我们都知道Weibo微博这个产品是没有群视频功能的,所以对于 groupVideoCall方法来说 Weibo 子类不能替代父类 SocialMedia。所以我们认为它是不符合里式替换原则。

如果强行这么做的话,会导致客户端用父类SocialMedia调用,但是实现类注入的可能是个Weibo的实现,调用groupVideoCall行为,产生意想不到的后果。

那有什么解决方案吗?

那就把功能拆开呗。

    public interface SocialMedia {   
       public void chatWithFriend(); 
       public void sendPhotosAndVideos() 
    }

    public interface SocialPostAndMediaManager { 
        public void publishPost(Object post); 
    }


    public interface VideoCallManager{ 
       public void groupVideoCall(String... users); 
    }

现在,如果您观察到我们将特定功能隔离到单独的类以遵循LSP。

现在由实现类决定支持功能,根据他们所需的功能,他们可以使用各自的接口,例如 Weibo 不支持视频通话功能,因此 Weibo 实现可以设计成这样:

    public class Instagram implements SocialMedia,SocialPostAndMediaManager{
    	public void chatWithFriend(){
        //logic
        }
        public void sendPhotosAndVideos(){
        //logic
        }
        public void publishPost(Object post){
        //logic
        }
    }

这样子就是符合里式替换原则LSP。

I—接口隔离原则

接口不应该强迫他们的客户依赖它不使用的方法。

大家可以看看自己的工程,是不是一个接口类中有很多很多的接口,每次调用API方法的时候IDE工具给你弹出一大堆,十分的"臃肿肥胖"。所以该原则的核心思想要将你的接口拆小,拆细,打破”胖接口“,不用强迫客户端实现他们不需要的接口。是不是和单一职责原则有点像?

例如,假设有一个名为 UPIPayment 的接口,如下所示

    public interface UPIPayments {
        
        public void payMoney();
        
        public void getScratchCard();
        
        public void getCashBackAsCreditBalance();
    }

现在让我们谈谈 UPIPayments 的一些实现,比如 Google PayAliPay

Google Pay 支持这些功能所以他可以直接实现这个 UPIPaymentsAliPay 不支持 getCashBackAsCreditBalance() 功能所以这里我们不应该强制客户端 AliPay 通过实现 UPIPayments 来覆盖这个方法。

我们需要根据客户需要分离接口,所以为了满足接口隔离原则,我们可以如下设计:

  • 创建一个单独的接口来处理现金返还。

    public interface CashbackManager{ 
    	public void getCashBackAsCreditBalance(); 
    }

现在我们可以从 UPIPayments 接口中删除getCashBackAsCreditBalanceAliPay也不需要实现getCashBackAsCreditBalance()这个它没有的方法了。

D—依赖倒置原则

高层模块不应该依赖低层模块,两者都应该依赖于抽象(接口)。抽象不应该依赖于细节(具体实现),细节应该取决于抽象。

这个原则我觉得也不是很好理解,所谓高层模块和低层模块的划分,简单来说就是,在调用链上,调用者属于高层,被调用者属于低层。比如大家都知道的MVC模式,controller是调用service层接口这个抽象,而不是实现类。这也是我们经常说的要面向接口编程,而非细节或者具体实现,因为接口意味着契约,更加稳定。

我们通过一个例子加深一下理解。

  • 借记卡

    public class DebitCard { 
    	public void doTransaction(int amount){ 
            System.out.println("tx done with DebitCard"); 
        } 
    }
  • 信用卡

    public class CreditCard{ 
    	public void doTransaction(int amount){ 
            System.out.println("tx done with CreditCard"); 
        } 
    }

现在用这两张卡你去购物中心购买了一些订单并决定使用信用卡支付

    public class ShoppingMall {
    	private DebitCard debitCard;
    	public ShoppingMall(DebitCard debitCard) {
            this.debitCard = debitCard;
       	}
    	public void doPayment(Object order, int amount){              
            debitCard.doTransaction(amount); 
     	}
    	public static void main(String[] args) {
         	DebitCard debitCard=new DebitCard();
         	ShoppingMall shoppingMall=new ShoppingMall(debitCard);
         	shoppingMall.doPayment("some order",5000);
        }
    }

上面的做法是一个错误的方式,因为 ShoppingMall 类与 DebitCard 紧密耦合。

现在你的借记卡余额不足,想使用信用卡,那么这是不可能的,因为 ShoppingMall 与借记卡紧密结合。

当然你也可以这样做,从构造函数中删除借记卡并注入信用卡。但这不是一个好的方式,它不符合依赖倒置原则。

那该如何正确设计呢?

  • 定义依赖的抽象接口BankCard

    public interface BankCard { 
      public void doTransaction(int amount); 
    }
  • 现在 DebitCardCreditCard 都实现BankCard

    public class CreditCard implements BankCard{
    	public void doTransaction(int amount){            
            System.out.println("tx done with CreditCard");
        }
    }

    public class DebitCard implements BankCard { 
    	public void doTransaction(int amount){ 
    		System.out.println("tx done with DebitCard"); 
        } 
    }
  • 现在重新设计购物中心这个高级类,他也是去依赖这个抽象,而不是直接低级模块的实现类

    public class ShoppingMall {
    	private BankCard bankCard;
    	public ShoppingMall(BankCard bankCard) {
            this.bankCard = bankCard;
        }
    	public void doPayment(Object order, int amount){
            bankCard.doTransaction(amount);
        }
    	public static void main(String[] args) {
            BankCard bankCard=new CreditCard();
            ShoppingMall shoppingMall1=new ShoppingMall(bankCard);
            shoppingMall1.doPayment("do some order", 10000);
        }
    }

我们还可以拿 Tomcat这个 Servlet 容器作为例子来解释一下。

Tomcat 是运行 Java Web 应用程序的容器。我们编写的 Web 应用程序代码只需要部署在Tomcat 容器下,便可以被 Tomcat 容器调用执行。按照之前的划分原则,Tomcat 就是高层模块,我们编写的 Web 应用程序代码就是低层模块。Tomcat 和应用程序代码之间并没有直接的依赖关系,两者都依赖同一个“抽象”,也就是 Sevlet 规范。Servlet 规范不依赖具体的 Tomcat 容器和应用程序的实现细节,而 Tomcat 容器和应用程序依赖 Servlet规范。

总结

本文总结了软件编程中的黄金原则,KISS原则,DRY原则,SOLID原则。这些原则不仅仅适用于编程,也可以指导我们在架构设计上。虽然其中有些原则很抽象,但是大家多多实践和思考,会体会到这些原则的精妙。

欢迎关注个人公众号【JAVA旭阳】交流学习

本文来自博客园,作者:JAVA旭阳,转载请注明原文链接:http://www.cnblogs.com/alvinscript/p/17433913.html

本文转载于网络 如有侵权请联系删除

相关文章

  • 几种 FPGA 芯片的工艺结构

    FPGA器件结构 1、可编程逻辑门阵列,由最小单元LE组成。2、可编程输入输出单元IOE。 3、嵌入式RAM块,为M4K块,每个的存储量为4K,掉电丢失。4、布线网络。5、PLL锁相环,EP4CE6E22C8N最大的倍频至250MHz,这也是该芯片的最大工作频率。1、基于SRAM结构的FPGA目前最大的两个FPGA厂商Altera公司和Xilinx公司的FPGA产品都是基于SRAM工艺来实现的。这种工艺的优点是可以用较低的成本来实现较高的密度和较高的性能;缺点是掉电后SRAM会失去所有配置,导致每次上电都需要重新加载。重新加载需要外部的器件来实现,不仅增加了整个系统的成本,而且引入了不稳定因素。加载过程容易受外界干扰而导致加载失败,也容易受“监听”而破解加载文件的比特流。虽然基于SRAM结构的FPGA存在这些缺点,但是由于其实现成本低,被广泛应用在各个领域,尤其是民用产品方面。2、基于反熔丝结构的FPGA目前FPGA厂商Actel公司的FPGA产品都是基于反熔丝结构的工艺来实现的,这种结构的FPGA只能编程一次,编程后和ASIC一样成为了固定逻辑器件。QuickLogic公司也有类似的

  • JS身份证真实性验证

    if(testId('320888888888888888')==false){ layer.msg("身份证号不正确!"); returnfalse; }复制//身份证真实性验证 functiontestId(value){ varvcity={ 11:"北京",12:"天津",13:"河北",14:"山西",15:"内蒙古", 21:"辽宁",22:"吉林",23:"黑龙江",31:"上海",32:"江苏", 33:"浙江",34:"安徽",35:"福建",36:"江西",37:"山东",41:"河南", 42:"湖北",43:"湖南",44:"广东",45:"

  • POJ 1844 Sum

    DescriptionConsiderthenaturalnumbersfrom1toN.Byassociatingtoeachnumberasign(+or-)andcalculatingthevalueofthisexpressionweobtainasumS.TheproblemistodetermineforagivensumStheminimumnumberNforwhichwecanobtainSbyassociatingsignsforallnumbersbetween1toN.ForagivenS,findouttheminimumvalueNinordertoobtainSaccordingtotheconditionsoftheproblem. InputTheonlylinecontainsinthefirstlineapositiveintegerS(0<S<=100000)whichrepresentsthesumtobeobtained. OutputTheoutputwillcontaintheminimumnumberNforwhichthe

  • 工作13年,我熬夜整理出了这些常用网站,吐血推荐

    大家好,我是高胜寒,遥想这十几年的职业生涯,我从java小工开始做起,一路走来,用过python,干过运维,做过CTO,也尝试过CEO,在传统行业留过痕,互联网行业刨过坑,教育行业留过名。虽算不上成功,但对互联网行业以及教育行业有了一些见解,今天我把这么多年经常用的网站整理出来分享给大家,希望在技术的道路上,大家越走越好。一.百度使用技巧1.pan.baidu.com从别人的网盘搜资料用法pan.bidu.con+内容比如要搜电影侏罗纪公园或者搜python教程pan.baidu.compython视频2.把搜索范围限定在某一个网站中sitek8ssite:csdn.netpythonsite:csdn.net这样搜出来的都是csdn里的内容,加别的网站也一样3.精准匹配:双引号和书名号“中国山西大学”如果查询词比较长,我们一般加引号,表示不要拆分。《手机》如果不加书名号,会出现手机,加了会出现电影python也一样,加了书名号会先找到书4.减号语法:在搜索结果中有些不想要的有时候我们搜一个资料,出来的广告比资料还多,这个时候就用到了减号的语法比如:linux-广告python-广告搜

  • 序列特征的处理方法之二:基于卷积神经网络方法

    前言上一篇文章介绍了基本的基于注意力机制方法对序列特征的处理,这篇主要介绍一下基本的基于卷积神经网络方法对序列特征的处理,也就是TextCNN方法。序列特征的介绍,背景以及应用可以参考上一篇的详细介绍,这里简单回顾一下定义,用户在使用APP或网站的时候,用户会产生一些针对物品的行为,比如点击感兴趣的物品,收藏或购买物品等,而这些行为往往代表着用户对这些物品是感兴趣的,而将这些交互过的物品放在时间轴来看,就形成了用户感兴趣的物品序列,我们要处理的数据对象类似如图1所示具有时序关系的序列特征,这里拿用户感兴趣的物品序列为例作为处理对象。▲图1.用户感兴趣的物品序列我们都知道用户的历史行为中可能存在着一些局部的连续行为,比如最近新型冠状病毒疫情严重,可能用户在过去几天内连续买了口罩和酒精这种消毒和防护用品,那么在推荐中可以根据这种局部信息来推荐一些跟防护和消毒相关的商品。而浅层的卷积神经网络由于其卷积感受野比较小,善于捕获局部信息,因此可以利用浅层卷积神经网络来对序列特征中的局部行为模式进行建模。鉴于针对句子序列建模的卷积神经网络TextCNN也是针对embeddingmatrix做处理,因

  • Windows下用Python实现Web

    今天想用python实现一个简单的WebService应用,从网上找了很多资料,却不能顺利配置工作环境,不过经过半天的努力,还是顺利解决各种Bug,记录一下。工作环境:Win7(64bit)+Python2.7.5(32bit)+Eclipse(HeliosServiceRelease1)配置WebService工作环境的步骤都类似,网上随便搜一下便可以搜到答案,我这里总结一下,主要分为以下几个步骤:1.下载PyInstaller工具注意:win764位必须使用ez_setup.py进行安装。方法是下载ez_setup.py后,在python环境下,直接运行ez_setup.py即可自动安装setuptools。然后把“C:\Python27\Scripts”添加到系统的环境变量Path中即可。网上也有人说可以直接运行exe类型的setup,自己没有试,感兴趣的可以自己试一下。复制2.下载WebService必备的库:Twisted,lxml,soaplib(依赖于Twisted和lxml库)复制如果正确安装了ez_setup.py,后面的工作就简单多了,只需要用“cmd”命令,

  • 8 spark之基本的Action操作 first, take, collect, count, countByValue, reduce, aggregate, fold,top

    转载自:https://blog.csdn.net/t1dmzks/article/details/70667011first返回第一个元素 scalascala>valrdd=sc.parallelize(List(1,2,3,3)) scala>rdd.first() res1:Int=1复制javaJavaRDD<Integer>rdd=sc.parallelize(Arrays.asList(1,2,3,3)); Integerfirst=rdd.first();复制takerdd.take(n)返回第n个元素 scalascala>valrdd=sc.parallelize(List(1,2,3,3)) scala>rdd.take(2) res3:Array[Int]=Array(1,2)复制javaJavaRDD<Integer>rdd=sc.parallelize(Arrays.asList(1,2,3,3)); List<Integer>take=rdd.take(2);复制collectrdd.coll

  • spring 后置处理器回调

    Spring初始化过程之invokeBeanFactoryPostProcessorsBeanFactoryPostProcessor详解讲述了BeanFactoryPostProcessor的使用与源码调用时机工厂后置处理器BeanFactoryPostProcessor阅读BeanFactoryPostProcessor详解BeanFactoryPostProcessor接口与BeanPostProcessor接口类似,可以对bean的定义(配置元数据)进行处理。也就是说,BeanFactoryPostProcessor调用的时候,bean的定义已经被解析成BeanDefinition,并保存在一个ConcurrentMap内(通过xml或注解的方式)。此时,一切Bean还没被初始化,仅仅只是有BeanDefinition。BeanFactoryPostProcessor的实现类,通过”order”控制执行次序(要实现Ordered接口)。后置处理器回调PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors

  • 设计模式之工厂方法模式(创建型)

    一、模式定义工厂方法模式:又称工厂模式,也叫虚拟构造器模式,属于构建型设计模式,工厂方法模式是在简单工厂模式上进行拓展,生产产品的过程由具体工厂类实现,基类只实现接口,这使得工厂方法模式可以在不修改工厂角色的情况下,引进新的产品。工作方法模式也符合”开闭原则“。工厂方法模式也称虚拟构造器(VirtualConstructor)模式或者多态工厂(PolymorphicFactory)模式二、模式结构工厂方法模式包含如下结构:Product:抽象产品ConcreteProduct:具体产品Factory:抽象工厂ConcreteFactory:具体工厂三、简单实例抽象工厂类:publicabstractclassPayMethodFactory { publicabstractAbstractPaygetPayMethod(); }复制具体工厂类:publicclassCashPayFactoryextendsPayMethodFactory { publicAbstractPaygetPayMethod() { returnnewCashPay(); } }复制客户端调用:PayMeth

  • 使用yield进行异步流程控制

    现状目前我们对异步回调的解决方案有这么几种:回调,deferred/promise和事件触发。回调的方式自不必说,需要硬编码调用,而且有可能会出现复杂的嵌套关系,造成“回调黑洞”;deferred/promise方式则对使用者而言简洁明了,在执行异步函数之前就已经构造好了执行链--then链,而且实现也很灵活,具体可参考Promise的实现;事件机制则是一种观察者模式的实现,但也必须硬编码在异步执行的函数中,当异步函数执行完毕后再trigger相关事件,而观察者则相应执行事件处理函数。 注意,刚刚提到了一个词--硬编码,依赖这种方式仅实现回调局限性很大,如在node中,对fs.readFile('file1','utf-8')完成之后再进行fs.readFile('file2','utf-8'),使用回调和事件触发则必须在第一个异步的回调函数中进行调用trigger,增强了这两个操作的强依赖,使用deferred/promise则会很好的避免。 现在,随着ECMAScript6的逐渐普及,我们可以在chro

  • C# 线程系列三 定时器线程

    上一篇文章我们讲诉了自定义线程执行器和任务处理器 我们继续来讲解自定义线程的定时执行器,我们在很多场景下需要做到某些状态或者数据进行更新,如果事情很多很杂,很时候时候会创建很多不同的定时器那么势必会照成系统的消耗和性能低下的问题!今天我们来解决这一问题。 首先我们创建定时任务执行器基类 1///<summary> 2/// 3///</summary> 4publicabstractclassTimerTaskBase:BaseTask 5{ 6 7 8///<summary> 9///开始执行的时间 10///</summary> 11publiclongStartTime{get;set;} 12 13///<summary> 14///是否一开始执行一次 15///</summary> 16publicboolIsStartAction{get;set;} 17 18///<summary> 19///结束时间 20///</summary> 21publiclongEndTime{

  • Chrome浏览器中Network 下边栏中 Finish 的含义与显示

    一、查看finish 需要点击页面左上角的刷新按钮后才能看到finish,直接切换页面模块无法显示     二、下边栏中的DOMContentLoaded和Load已经非常熟悉。  DOMContentLoaded和Load分别对应页面DOMContentLoaded和Load事件触发的时间点。   DOMContentLoaded: DOM树构建完成。 即HTML页面由上向下解析HTML结构到末尾封闭标签。   LOAD 页面加载完毕。 DOM树构建完成后,继续加载html/css中的图片资源等外部资源,加载完成后视为页面加载完毕。 其中,DOMContentLoaded会比Load时间小,两者时间差大致等于外部资源加载的时间。   Finish Finish时间与DOMContentLoaded和Load并无直接关系。 Finish时间是页面上所有http请求发送到响应完成的时间,HTTP1.0/1.1协议限定,单个域名的请求并发量是6个,即Finish是所有请求(不只是XHR请求,还包括DOC,img,js,css等资

  • react中Redux应用框架学习

    1.最普通的react-redux   2.应用context的傻瓜组件和聪明组件的redux框架      3.精简版react-redux,利用react-redux模块的redux(推荐)    4.多个模块之间的应用  

  • mac双系统出现动态磁盘的解决

    给朋友的苹果装双系统,装完后不小心在windows里把分区转成了动态磁盘,无法进入系统了1、使用u盘引导进入win8pe,打开DiskGenius,搜索丢失分区,搜索到全部保留,应该可以搜索到EFI和bootcamp这两个分区,保存更改后重启可以进入windows2、此时按照网上的方法再次进入pe把bottcamp之前的未分配空间新建分区是无法启动mac的,这时间应该进入windows下载安装R-Studio,打开R-Studio后会显示出以前的被删除的分区,我的被标记为Disk1-01,Disk1-02,Disk1-03,Disk1-043、记下Disk1-02,Disk1-03的分区偏移和分区大小4、再次进入pe打开DiskGenius,根据记下的数值把bootcamp前面的未分配空间分配为两个主分区,标记选择为AF5、保存重启即可进入mac  

  • UnityShader-菲涅尔反射(Fresnel Reflection)

    菲涅耳公式(或菲涅耳方程),由奥古斯丁·让·菲涅耳导出。用来描述光在不同折射率的介质之间的行为。由公式推导出的光的反射称之为“菲涅尔反射”。菲涅尔公式是光学中的重要公式,用它能解释反射光的强度、折射光的强度、相位与入射光的强度的关系 在计算机图形学中的应用## 一般运用于水面效果,试想一下你站在湖边,低头看向水里,你会发现近的地方非常清澈见底(反射较少),而看远的地方却倒映着天空(反射较多)。这就是菲尼尔效应 效果## 这里直接让反射颜色为红色,可以看到远处的更红,而近处的为光照颜色白色 简化后的公式## 由于真实的菲尼尔公式计算量较多。在游戏里往往会用简化版的公式来提升效率达到近似的效果 fresnel=fresnel基础值+fresnel缩放量*pow(1-dot(N,V),5) 复制 Shader实现## Shader"lijia/fresnelTest" { Properties { _MainTex("Texture",2D)="white"{} _fresnelBase("fresnelBase",Range(0,1))=1 _fresnelScal

  • ASPxGridView的几个使用方法

    一。ASPXGridView外观显示属性:Caption----列的标题(KeyFieldName----数据库字段SEOFriendly是否启用搜索引擎优化Summary指定分页汇总信息的格式 Setting节点的ShowFilterRow=True设置快速查找功能 SettingsBehavior.AllowFocusedRow=true高亮选中的行,即选中行变色SettingsBehavior.AllDragDrop=false禁止拖动标题列头SettingsBehavior.AllowSort实现列头点击后是否可以排序 SettingsPager.ShowEmptyDataRows=True;当数据行为空时,显示空行SettingsPager.PageSize每页显示的记录总数AllButton.Text“全部数据显示”按钮的文本AllButton.Visible 是否显示“全部数据显示”按钮FirstPageBuotton/LastPageButton/NextPageButton/PrevPageButton/对应首页、末页、下页、上页,设置同上。Num

  • maven项目引入自定义SDK包

    1.将jar放到项目根目录(lib文件夹下)   2.修改pom文件   <dependency> <groupId>*******</groupId> <artifactId>*******</artifactId> <version>1.1-SNAPSHOT</version> <scope>system</scope> <systemPath>${project.basedir}/lib/*******-1.1-20220629.060335-9.jar</systemPath> </dependency> 复制   3.修改package配置 <build> <resources> <!--将项目根目录下的lib文件中的jar包全部打入BOOT-INF/lib文件下--> <resource> <directory>lib</dir

  • Docker实践:创建并运行一个自定义的Docker Image

    1目标: 创建一个自定义的Image,在该Image中,包含一个自己的Python程序,通过docker运行该Image容器,并执行我们自己的Python程序,通过添加数据卷,在本机能够查看Python的执行结果. 2创建Dockerfile 创建自定义Image,需要定义一个Dockerfile. 2.1Dockerfile语法规则  8使用Dockerfile创建镜像    dockerfile:文本格式的配置文件        可以使用dockerfile创建自定义的镜像    8.1基本结构        四部分            基础镜像信息:FROMcentos 基于的基础镜像      &nbs

  • wordpress修改或删除后台底部的”感谢使用WordPress进行创作。”文字

    WordPress后台底部有一行小字:“感谢使用WordPress进行创作。”,表明这是使用WordPress搭建的一个网站,如果不想显示该文字或想修改为其它文字,可以使用下面的代码移除。 编辑当前主题的functions.php文件,在第一行<?php下面添加以下代码(二选一): 1、修改文字 1 2 3 4 复制 functionfooterText(){ return'这是<ahref="https://www.boke8.net">博客吧网站</a>后台,使用的是<ahref="http://wordpress.org/">WordPress</a>程序'; } add_filter('admin_footer_text','footerText',9999);复制 2、删除文字 1 2 3 4 复制 functionfooterText(){ return''; } add_filter('admin_footer_text','footerText',9999);复制

  • location对象

    location对象属性 hash  返回#后跟0或者多个字符串,没有#或者#后没有字符则返回空字符串  '#page=2' host  返回服务器名称和端口号  "www.createhy.com:8888" hostname  返回服务器名称  "www.createhy.com" href  返回完整的url  "http://www.createhy.com:8888/CR8000Web/",location.toString()也返回此值。 pathname  返回url中的目录和文件名  "/CR8000Web/" port  返回端口号,如果不含,则返回空  "8888" protocol  返回协议,一般是http或者https  "http:" search  返回地址栏中'?'及后续字符串(查询字符串)  "?dd=ee" 查询字符串函数: functionsetQueryStringArgs(){     varqs=location.search.length>0?location.search.substring(1):'',       args=

  • 需求分析(软工造梦厂)

    这个作业属于哪个课程 课程 这个作业要求在哪里 作业要求 团队名称 软工C#造梦厂 这个作业的目标 实现需求分析,完善分工任务,设计项目原型 1、项目人员 姓名 学号 张旭(组长) 201731024123 周成杰 201731024136 邹扬锋 201731024134 赵俊安 201731024121 黄涛 201731024119 陈欣 201731024102 马芸慧 201731024104 马昊妍 201731024107 2、项目简介   黄金点小游戏:黄金点游戏规则:N(大于等于10)个玩家进行游戏,每名玩家从0到100(不能选0和100)之间选择一个有理数,将所有玩家的数值求平均值G,将G乘以0.618,即黄金分割比例,得出S值(黄金点),离S值最近的玩家得N分,最远的玩家得-2分,其余玩家不得分;每名玩家的初始分值定位一百分。重复N轮游戏,得分最高的玩家获胜。 3、典型用户   作为一个益智类小游戏,需求人群是十分广大的,可以说老少咸宜,不过我们认为主要需求人群还是以学生、上班族等压力较大

相关推荐

推荐阅读