前言
2017年過了大半個月,才總結了第一篇博客,也真是有夠懶的~~哈哈,解釋解釋~主要最近的筆記都零零散散的,開發又緊,所以得慢慢總結匯總成筆記和博客。還記得2016年底被公司安排學習web,悲劇的是學習了不到一星期,由於公司項目開發週期安排緊張,又被調回來Android開發,看來我和Android機器人還是緣分依舊。新技術的潮流趨勢,多瞭解些技術和開發語言也是非常重要的,重在學習,所以會在力所能及的情況下繼續學習web,最近也抽了點時間看看設計模式相關的書籍,那麼其中就包含面向對象五大原則,又剛好前幾天看到一網站博文關於面向對象五大原則在Android中的運用,邊看邊總結,結合書和博文,就有了2017年第一篇博客總結,day day up!!!
簡述
SOLID是面向對象五大基本原則的縮寫,分別是:
- 單一職責原則
- 開閉原則
- 里氏替換原則
- 接口隔離原則
- 依賴倒置原則
在日常的項目開發中,隨着項目的不斷迭代,越感覺這些原則對於編程是多麼的重要,所以還是有必要抽空學習的
Ⅰ.單一職責原則
Android日常的開發經常會遇到條目佈局,隨着Android5.0的普及,原先使用到的ListView大多被RecyclerView所替代,而在使用RecyclerView時需要爲其條目設置數據或是多類型條目,那麼就需要爲RecyclerView設置適配器,使用RecyclerView適配器則需要重寫三個方法,分別是onCreateViewHolder、onBindViewHolder和getItemCount.
public class RvAdapter extends RecyclerView.Adapter{
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//找尋佈局的控件,生成ViewHolder
return null;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
//將數據顯示在控件上
}
@Override
public int getItemCount() {
return 0;
}
}
上面三個方法應該都是非常熟悉的,可以在onCreateViewHolder去將佈局設置給holder(佈局持有者,作用是找到佈局上的控件),在onBindViewHolder方法主要操作是將數據顯示在控件上,而getItemCount則返回條目數量,這就是三個方法各自的職責,也就是各自遵守的單一職責,隨便看下下面簡單的操作。
//RvAdapter類
public class RvAdapter extends RecyclerView.Adapter{
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View rootView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.layout_item, parent, false);
return new RvViewHolder(rootView);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
RvViewHolder rvHolder = (RvViewHolder)holder;
rvHolder.mTvNumber.setText(models.get(position).getNumber());
rvHolder.mTvPelf.setText(models.get(position).getPelf());
}
@Override
public int getItemCount() {
return models == null? 0 : models.size();
}
}
//ViewHolder類
public class RvViewHolder extends RecyclerView.ViewHolder{
private TextView mTvNumber;
private TextView mTvPelf;
public RvViewHolder(View itemView) {
super(itemView);
initView();
}
private void initView() {
mTvNumber = (TextView)itemView.findViewById(R.id.tv.number);
mTvPelf = (TextView)itemView.findViewById(R.id.tv.pelf);
}
}
//Model類
public class Model{
String number;
String pelf;
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public String getPelf() {
return pelf;
}
public void setPelf(String pelf) {
this.pelf = pelf;
}
}
適配器重寫的三個方法各自的職責操作就跟上面的類似了,每個方法遵循着各自的職責,那麼假如現在產品mm來了個新需求,需要對上面的model數據的金額進行公式計算,計算完成之後才能設置在控件上,那麼按照日常的開發Code,可愛的工程師們或許會這麼寫;
//RvAdapter類
public class RvAdapter extends RecyclerView.Adapter{
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View rootView = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_item, parent, false);
return new RvViewHolder(rootView);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
RvViewHolder rvHolder = (RvViewHolder)holder;
//對金額進行計算
String result = String.valueOf(Integer.valueOf(models.get(position).getNumber())*0.8)
+(Integer.valueOf(models.get(position).getNumber()) * 0.75 + 100);
rvHolder.mTvNumber.setText(result);
rvHolder.mTvPelf.setText(models.get(position).getPelf());
}
@Override
public int getItemCount() {
return models == null? 0 : models.size();
}
有木有覺得上面在onBindViewHolder方法中的操作就違反了單一職責原則,在onBindViewHolder裏先做金額數據的運算操作,數據計算完成之後再設置在控件,數據的操作與控件的顯示數據這兩個操作,該方法中做的就不再是單一的任務,當某天產品又來了新需求,需要增加一個顯示收益的數據,還得根據金額計算出收益,那麼關於收益的代碼是不是類似上面一樣在onBindViewHolder方法裏進行書寫,隨着需求越來越多,那麼代碼就堆成一團咯,可能你也揪心,後面維護的小夥伴看着都揪心…話說回來,又該怎麼優化上面關於金額數據的運算及顯示呢?看下面代碼,讓你豁然開朗
//RvAdapter類
public class RvAdapter extends RecyclerView.Adapter{
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View rootView = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_item, parent, false);
return new RvViewHolder(rootView);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
RvViewHolder rvHolder = (RvViewHolder)holder;
rvHolder.mTvNumber.setText(models.get(position).getNumber());
rvHolder.mTvPelf.setText(models.get(position).getPelf());
}
@Override
public int getItemCount() {
return models == null? 0 : models.size();
}
//Model類
public class Model{
String number;
String pelf;
public String getNumber() {
String result = String.valueOf(Integer.valueOf(number)*0.8)
+(Integer.valueOf(number) * 0.75 + 100);
return result;
}
public void setNumber(String number) {
this.number = number;
}
public String getPelf() {
return pelf;
}
public void setPelf(String pelf) {
this.pelf = pelf;
}
}
上面的適配器裏關於數據運算的操作移到Model進行計算了,當執行getNumber方法時,再進行數據的運算操作,剛纔假設之後新增需求計算收益數據,那麼同樣的,在model進行計算並提供get方法,是不是挺簡單明瞭的.在onCreateViewHolder去將佈局設置給holder,在onBindViewHolder方法主要操作是將數據顯示在控件上,getItemCount則返回條目數量,各自的方法,各自的職責,保持互不干擾.
總結:在開發中,應該使得方法和類保持單一職責原則,這樣便於維護而且邏輯清晰
Ⅱ.開閉原則
簡述:開閉原則的核心思想是避免修改程序,讓程序更加具有擴展性;往往會覺得這不是很矛盾?當你瞭解開閉原則後,其實並並不會矛盾;日常開發中,java的繼承與實現是很常見的,都是體現開閉原則的一大亮點,可能開發中遇到而不知道這代碼就是遵循了開閉原則.
那麼從例子說起吧,畢竟有例子會比較易懂.假設有這樣的需求:計算任何形狀的多邊形的面積總和,可能由於業務剛剛開始,前期的多邊形都是長方形,那麼親愛的開發工程師,該怎麼寫呢?
//長方形
class Rectangle{
private double width;
private double height;
public double getWidth() {
return width;
}
public void setWidth(double width) {
this.width = width;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
}
//所有多邊形的面積管理類
class AreaManager{
public double computeArea(List<Rectangle> shapes){//負責計算多邊形面積總和
double areas = 0;
for(Rectangle rectangle:shapes){
areas += (rectangle.getWidth() * rectangle.getHeight());
}
return areas;
}
}
上面的代碼解決了所有多邊形爲長方形情況下,它們的面積總和的需求。現在假設隨着業務逐步擴展,出現了圓形這一角色,很明顯上面的代碼已然不再適用,那麼理所當然的對上面的代碼進行更改吧
//圓形
class Circle{
private double radiu;
public double getRadiu() {
return radiu;
}
public void setRadiu(double radiu) {
this.radiu = radiu;
}
}
//所有多邊形的面積和管理類
class AreaManager{
public double computeArea(List<Object> shapes){//負責計算多邊形面積總和
double areas = 0;
for(Object shape:shapes){
if(shape instanceof Rectangle){
Rectangle rectangle = (Rectangle)shape;
areas += (rectangle.getWidth() * rectangle.getHeight());
}else if(shape instanceof Circle){
Circle cirlce = (Circle)shape;
areas += (cirlce.getRadiu() * cirlce.getRadiu() * Math.PI);
}else{
throw new RuntimeException("Shape not supported");
}
}
return areas;
}
}
如果每次新的多邊形角色出現,都得對上面的代碼進行if判斷和修改,是不是很煩而且代碼也不好維護,久而久之,代碼可能就亂成一團了,而上面每次的修改也違反了開閉原則,擴展新功能是基於舊代碼修改的前提下進行的,這樣肯定是不行的,那麼又該怎麼去避免這種修改呢?由於java面向對象的特性,所以避免這種修改也是挺方便避免的,看代碼.
interface Shape{
//用於計算多邊形的面積
double getArea();
}
//長方形
class Rectangle implements Shape{
private double width;
private double height;
@Override
public double getArea() {
return width * height;
}
}
//圓形
class Circle implements Shape{
private double radiu;
@Override
public double getArea() {
return radiu * radiu * Math.PI;
}
}
//所有多邊形的面積總和計算管理類
class AreaManager{
public double computeArea(List<Shape> shapes){
double areas = 0;
for(Shape shape:shapes){
areas += shape.getArea();
}
return areas;
}
}
//主函數
public static void main(String args){
List<Shape> shapes = new ArrayList<>();
shapes.add(new Rectangle(10, 10));
shapes.add(new Rectangle(6, 6));
shapes.add(new Circle(6));
AreaManager.computeArea(shapes);
}
假設現在的業務中又出現了正方形這角色,上面的代碼AreaManager類的代碼還需要進行修改嗎?顯然是不需要的,因爲計算面積的方法已經抽象到Shape接口裏,任何多邊形只要實現Shape接口,重寫getArea方法coding關於計算面積的邏輯代碼,這樣就解決了不同多邊形的面積計算的問題。再看看AreaManager類,只有computeArea這個方法,參數是裝Shape接口實現類的List集合,而各種多邊形的面積由於計算公式並不同,顯然不能在computeArea方法裏實現,所以多邊形自己去實現吧,那麼只要在AreaManager類的computeArea方法裏去遍歷各個多邊形,獲取其面積getArea()進行累加,這樣就簡單多了~~
//新增正方形
class Square implements Shape{
private double length;
public Square(double length) {
this.length = length;
}
@Override
public double getArea() {
return length * length;
}
}
//主函數
public static void main(String args){
List<Shape> shapes = new ArrayList<>();
shapes.add(new Rectangle(10, 10));
shapes.add(new Rectangle(6, 6));
shapes.add(new Circle(6));
shapes.add(new Square(5));
shapes.add(new Square(9));
AreaManager.computeArea(shapes);
}