2014年4月13日 星期日

[Code Review] Team 09 - Hw06

在此超有誠意的附上整個專案檔
https://drive.google.com/file/d/0B9vj2TjBHPutWVBablNsVU5NNTJ0MTdmd0lrQjRGQ0FlbU00/edit?usp=sharing



Form1 開頭部分(一開始忘記貼,4/14 am03:47 補上)
private List<Shape> ListShape = new List<Shape>();

public Form1()
{
    InitializeComponent();
}

private double DensityTable(string p_Material)
{
    if (p_Material == "鐵") return  13;
    if (p_Material == "冰") return  1;
    if (p_Material == "木材") return 0.8;
    return -9999;
}

加入新Shape按鈕
private void btnAddNewShape_Click(object sender, EventArgs e)
{
    ///================ 使用者輸入檢查 開始 ================
          
    bool CheckPass = false; //標記使用者是否通過防呆檢查
    string Forget = "";     //存放使用者忘記輸入的項目
    int DummyInt;           //供int.TryParse()檢查用
    double DummyDouble;     //供double.TryParse()檢查用

    if (null == cbBox_Material.SelectedItem)Forget += " 材質";
    if (null == cbBox_Shape.SelectedItem) Forget += " 形狀";
    if (!int.TryParse(txtBox_AddNewShapeNum.Text, out DummyInt)) Forget += " 數量";
    if (!double.TryParse(txtBox_ShapeParameter1.Text, out DummyDouble)) Forget += " 第1個形狀參數";
    if ((cbBox_Shape.SelectedItem == "圓柱體") || (cbBox_Shape.SelectedItem == "金字塔"))
    {
        if ("" == txtBox_ShapeParameter2.Text) Forget += " 第2個形狀參數";
    }

    //拋出檢查結果. 若無錯誤就Pass
    if ("" != Forget)
        MessageBox.Show("你忘記輸入" + Forget + " !\a");
    else
        CheckPass = true;

    ///================ 使用者輸入檢查 結束 ================

    //若是沒有通過防呆檢查,以下這個迴圈會被by pass
    for(int i=0; CheckPass && i<Convert.ToInt16(txtBox_AddNewShapeNum.Text);i++)
    {
        if (cbBox_Shape.SelectedItem == "球")
        {
            double tempRadius = Convert.ToDouble(txtBox_ShapeParameter1.Text);
            ListShape.Add(new Ball(tempRadius, DensityTable(cbBox_Material.Text)));

            //顯示在txtBox_ShapeList
            txtBox_ShapeList1.AppendText("Ball "
                + "半徑="+ Convert.ToString(tempRadius) +" "
                + "\n");
        }

        if (cbBox_Shape.SelectedItem == "正方體")
        {
            double tempSide = Convert.ToDouble(txtBox_ShapeParameter1.Text);
            ListShape.Add(new Cube(Convert.ToDouble(txtBox_ShapeParameter1.Text), DensityTable(cbBox_Material.Text)));

            //顯示在txtBox_ShapeList
            txtBox_ShapeList1.AppendText("Cube "
                + "邊長=" + Convert.ToString(tempSide) + " "
                + "\n");
        }

        if (cbBox_Shape.SelectedItem == "圓柱體")
        {
            double tempRadius = Convert.ToDouble(txtBox_ShapeParameter1.Text);
            double tempHeight = Convert.ToDouble(txtBox_ShapeParameter2.Text);
            ListShape.Add(new Cylinder(tempRadius, tempHeight, DensityTable(cbBox_Material.Text)));

            //顯示在txtBox_ShapeList
            txtBox_ShapeList1.AppendText("Cylinder "
                + "半徑=" + Convert.ToString(tempRadius) + " "
                + "高度=" + Convert.ToString(tempHeight) + " "
                + "\n");
        }

        if (cbBox_Shape.SelectedItem == "金字塔")
        {
            double tempSide = Convert.ToDouble(txtBox_ShapeParameter1.Text);
            double tempHeight = Convert.ToDouble(txtBox_ShapeParameter2.Text);
            ListShape.Add(new Pyramid(tempSide, tempHeight, DensityTable(cbBox_Material.Text)));

            //顯示在txtBox_ShapeList
            txtBox_ShapeList1.AppendText("Pyramid"
                + "邊長=" + Convert.ToString(tempSide) + " "
                + "高度=" + Convert.ToString(tempHeight) + " "
                + "\n");
        }

    }
}

 
========================================================
形狀選單更動
private void cbBox_Shape_SelectedIndexChanged(object sender, EventArgs e)
{//顯示介面控制, 當使用者改變 形狀 時, 動態更動UI
    if (cbBox_Shape.SelectedItem == "球")
    {
        labelParameter1.Text = "直徑"; txtBox_ShapeParameter1.Visible = true;
        labelParameter2.Text = "";     txtBox_ShapeParameter2.Visible = false;
    }

    if (cbBox_Shape.SelectedItem == "正方體")
    {
        labelParameter1.Text = "邊長"; txtBox_ShapeParameter1.Visible = true;
        labelParameter2.Text = "";     txtBox_ShapeParameter2.Visible = false;
    }

    if (cbBox_Shape.SelectedItem == "圓柱體")
    {
        labelParameter1.Text = "直徑"; txtBox_ShapeParameter1.Visible = true;
        labelParameter2.Text = "高";   txtBox_ShapeParameter2.Visible = true;
    }

    if (cbBox_Shape.SelectedItem == "金字塔")
    {
        labelParameter1.Text = "邊長"; txtBox_ShapeParameter1.Visible = true;
        labelParameter2.Text = "高";   txtBox_ShapeParameter2.Visible = true;
    }
}
=========================================================
 計算按鈕
private void btnCalcShapeParameter_Click(object sender, EventArgs e)
{
    txtBox_ShapeList2.Clear();

    for (int i = 0; i < ListShape.Count ; i++)
    {
        txtBox_ShapeList2.AppendText( ListShape[i].getObjName() + " "
                                    + "密度:" + ListShape[i].Density + " "
                                    + "體積:" + ListShape[i].getBulk() + " "
                                    + "重量:" + ListShape[i].getWeight() + " "
                                    + "\n");
    }
}
=========================================================
Shape類別(其他類別的基礎類別)
abstract class Shape
{//此為抽象類別,不可以實體化,因為形狀不是任何一種形狀
    private static int count = 0;
    protected static double Pi = 3.14159;
    protected double density=0;
    public Shape Next = null;

    abstract public string getObjName();//每個形狀名字不一樣, 留給衍生類實作

    public Shape()
    {
        count++;
    }

    ~Shape()
    {
        count--;
    }

    public static int Count
    {//從物件外只能get, 不能set
        get { return count; }
    }

    public double Density
    {//這是屬性
        get {return density; }
    }
        

    public double getWeight()
    {//重量=體積*密度, 不須另外宣告變數存放
        return getBulk() * density;
    }

    public abstract double getBulk();//體積演算法每種形狀都不同, 留給衍生類實作
}
=========================================================
Ball類別(繼承自Shape)
class Ball : Shape
{
    public double radius=0;
    private static int count = 0;

    public override string getObjName() { return "Ball"; }

    public Ball(double p_radius, double p_density)
    {
        this.density = p_density;
        this.radius = p_radius;
        count++;
    }

    ~Ball()
    {
        count--;
    }

    new public static int Count
    {//從物件外只能get, 不能set
        get { return count; }
    }

    public override double getBulk()
    {
        return (4.0 / 3.0) * Pi * radius * radius * radius;
    }
}
=========================================================
Cube類別(繼承自Shape)
class Cube : Shape
{
    public double side;
    private static int count = 0;

    public override string getObjName() { return "Cube"; }

    public Cube(double p_side, double p_density)
    {
        this.density = p_density;
        this.side = p_side;
        count++;
    }

    ~Cube()
    {
        count--;
    }

    new public static int Count
    {//從物件外只能get, 不能set
        get { return count; }
    }

    public override double getBulk()
    {
        return side*side*side;
    }
}
=========================================================
Cylinder類別(繼承自Shape)
class Cylinder : Shape
{
    public double radius;
    public double height;
    private static int count = 0;

    public override string getObjName() { return "Cylinder"; }

    public Cylinder(double p_radius, double p_height, double p_density)
    {
        this.density = p_density;
        this.radius = p_radius;
        this.height = p_height;
        count++;
    }

    ~Cylinder()
    {
        count--;
    }

    new public static int Count
    {//從物件外只能get, 不能set
        get { return count; }
    }

    public override double getBulk()
    {
        return Pi * radius * radius * height;
    }
}
=========================================================
Pyramid類別(繼承自Shape)
class Pyramid : Shape
{
    public double side;
    public double height;
    private static int count = 0;

    public override string getObjName() { return "Pyramid"; }

    public Pyramid(double p_side, double p_height, double p_density)
    {
        this.density = p_density;
        this.side = p_side;
        this.height = p_height;
        count++;
    }

    ~Pyramid()
    {
        count--;
    }

    new public static int Count
    {//從物件外只能get, 不能set
        get { return count; }
    }

    public override double getBulk()
    {
        double Dside = side/1000;
        double Dheight = height/1000;
        double tempBulk=0;

        for (int i=0; i<1000 ; i++ )
        {//從最底下的那片體積往上加,邊長一直縮小
            double CurrentSide = side - (i * Dside * 0.5); //0.5 使 side 修正到斜邊中點
            tempBulk += CurrentSide * CurrentSide * Dheight;
        }

        return tempBulk;
    }
}

12 則留言:

  1. 林高遠:這是我寫的,使用者錯誤檢查那邊,我用的是類似<大話設計模式>中提的 範本方法模式(Template Method),只是沒跨類別實作

    請大家快來批評我,謝謝

    回覆刪除
  2. 第8組
    錯誤檢查判斷的好仔細(Y)
    但可以請問 Class Shape 內的 public Shape Next = null;
    這行的Next成員的功用嗎 ?

    回覆刪除
    回覆
    1. 我本來要自幹Linklist,但是搞不定傳址,所以這是Linklist難產的闌尾,可割可棄

      刪除
  3. [第六組]
    檢查輸入錯誤的部分真的不錯!
    第一塊程式碼:28行for迴圈內的檢查,用switch或者if...else應該會比較有效率. 因為當使用者選擇"球"時,現在的寫法就算跑完了球的處理部分,還是會再往下繼續判斷每個if,有點浪費時間(雖然if判斷很快...).
    第二塊程式碼:選單隱藏或顯現的變動,這樣寫也可以. 只是一樣要注意一樣全部只用if和使用switch或if...else的差別.

    回覆刪除
    回覆
    1. 謝謝你,你說的缺點我有注意到

      因為我不是用數字做選項判斷,我是用字串,所以不能用switch case;我認為在x86上用字串判斷維持程式可讀性,比省下幾個 if else 判斷時間來的更好

      刪除
  4. 1. 原來這才是正宗版本,難怪上一個版本那麼驚悚...。
    2. 整個架構包含註解都相當完整,看起來真的是賞心悅目,我認為把防呆放在一起處理也是一個好設計。
    3. 注意一下防呆部分,不是真的轉得過去就OK了,這個程式碼會讓參數為負值的照樣通過,需要再一點點的判斷。
    4. 看到數值積分出來都囧了,你要不要google一下角錐體積怎麼算。

    回覆刪除
    回覆
    1. 1. 我想版本或許有高低之分,但無所謂正宗或不正宗。因為,就算寫得再差,那還是我們組員的作品
      3. 沒錯, 數量不能是負的, 謝謝提醒
      4. 這段程式碼在火車上寫的, 沒網路, 所以就只好硬幹了...

      刪除
  5. 作者已經移除這則留言。

    回覆刪除
  6. (第九組)
    1.形狀設定為球或是圓柱體時,輸入欄位的名稱應該是半徑,而不是直徑
    2.輸入數量的地方有點多餘,因為計算的參數都一樣,同樣的東西不需要計算這麼多遍

    回覆刪除
  7. 衍生類別中的count為何不用加new(隱藏繼承)??跟你所用的虛擬類別有關嗎?

    回覆刪除