查看完整版本 : OOD 難唔難?

fitcat07 2017-11-10 22:58

早前發過帖,我話OOD難。可能呢度高人特別多,回帖嘅都唔認同。:smile_13:

自覺OOD唔多好,上緊MOOC堂:
[url=https://www.edx.org/course/software-construction-object-oriented-ubcx-softconst2x]https://www.edx.org/course/software-construction-object-oriented-ubcx-softconst2x[/url]

一題練習要重構Busy'sDiner project(由Busy'sDiner1到Busy'sDiner3)。做完練習,發覺答案嘅設計唔好。以下2條係我完成嘅練習(Busy'sDiner/Busy'sDiner3)同課堂答案github連結(使用IntelliJ IDE):
[url=https://github.com/fitcat/long-form-problem-solutions.git]https://github.com/fitcat/long-form-problem-solutions.git[/url]
[url=https://github.com/UBCx-Software-Construction-2/long-form-problem-solutions.git]https://github.com/UBCx-Software-Construction-2/long-form-problem-solutions.git[/url]

以下是我在課堂上論壇嘅帖子,暫時未有回覆:

I prefer the use of interface instead of inheritance on Host, Server and FOHEmployee. I defined Servable (replacement of FOHEmployee) as an interface with default implementation on some methods (supported by Java 9). Both Host and Server implements it.

In the solution, FOHEmployee has an association to Dish. I think it violates single responsibility principle (SRP)- FOHEmployee should not has a dish or contain a dish. Moreover, DISH_PRICE and currentOrderNumber should not be defined in Server (SRP). They should be defined in Dish and Order, respectively to increase high cohesion and low coupling.

The field orders in Server should be removed as it is not used. I think it is an over design. Similarly for cash. I changed the method takePayment to return the cash received.

With the above change, both the static methods doFOHOrderRoutine and doServerOrderRoutine are no longer required. These 2 methods require knowledge of the internal implementation of Order and Server, which makes high coupling. I abstracted their functionalities as a method in Servable. In Diner, both host and server are of type Servable. A call to host.doOrderRoutine() and/or server.doOrderRoutine() is used instead. This reduces the complexity of the client (Diner), makes it looks more cleaner and less coupling.

望各位指教。

Susan﹏汪汪 2017-11-10 23:57

題目係咩?

assembly.jc 2017-11-11 11:28

是 OOD 因難,還是設計大型系統因難,還是用 OOD 去 設計大型系統因難?

營運大型系統的難點有幾個
1. 維護難。requirement 經常變。
2. 管理難。控制人,時程,testing, deployment,training,support ...
3. Trouble shooting 難。要快速找到問題所在並作出補救。
4. 修復難。特別是渉及 Database,修復 Data 比 修復 Program 難。

OOD 可能唔難,但用 OOD 去解決上述問題時,就變得難,部份原因可能是半數問題不是 design 問題,而是管理問題,Database 問題,security 問題等等。
另外,實務上,著重「變」,換成 design 的術語就是 "Coupling"。如果改小部 requirement 就要推倒整個設計,咁不如沒有設計。
最後,是尋找問題的能力,大型系統,source code 動輒數十萬行,有問題時去 source code 找,去看 documentation? OOD 有方法能快速指出問題所在? 還是靠 unit test, regression test。

Susan﹏汪汪 2017-11-11 11:46

[quote]原帖由 [i]assembly.jc[/i] 於 2017-11-11 11:28 AM 發表 [url=http://www.discuss.com.hk/redirect.php?goto=findpost&pid=470708017&ptid=27050301][img]http://www.discuss.com.hk/images/common/back.gif[/img][/url]
是 OOD 因難,還是設計大型系統因難,還是用 OOD 去 設計大型系統因難?

營運大型系統的難點有幾個
1. 維護難。requirement 經常變。
2. 管理難。控制人,時程,testing, deployment,training,support ...
3 ... [/quote]
汪汪啱先諗到有趣論點

如果把大型系統的codebase想像成database一樣
你要搜尋指定結果的快捷方法只有分區搜尋

只要你能夠把問題的scope收窄
然後直接定位到某個library 甚至某個function
呢個先係有效解決問題的方法

倒轉頭講
OOD定其他design pattern
都係主要講內聚性和耦合性

目的都係呢個原因

fitcat07 2017-11-11 13:36

[quote]原帖由 [i]Susan﹏汪汪[/i] 於 2017-11-11 11:46 AM 發表 [url=http://computer.discuss.com.hk/redirect.php?goto=findpost&pid=470708729&ptid=27050301][img]http://computer.discuss.com.hk/images/common/back.gif[/img][/url]
倒轉頭講
OOD定其他design pattern
都係主要講內聚性和耦合性

目的都係呢個原因 [/quote]
冇錯!所有design principles,目的就係達致高cohesion,低coupling。

話說回頭,全部初畢業生,都知道系統要有maintainability,extensibility,security,...但知還知,做就做唔到。原因係因為要做到好困難:
[url=https://www.cnet.com/news/62-percent-of-it-projects-fail-why/]62 percent of IT projects fail. Why?[/url]

情況就好似上緊嘅MOOC堂,有講SRP, high cohesion and low coupling,但係提供嘅答案都做唔到。

fitcat07 2017-11-11 13:39

[quote]原帖由 [i]Susan﹏汪汪[/i] 於 2017-11-10 11:57 PM 發表 [url=http://computer.discuss.com.hk/redirect.php?goto=findpost&pid=470694273&ptid=27050301][img]http://computer.discuss.com.hk/images/common/back.gif[/img][/url]
題目係咩? [/quote]
題目好長,要分幾個帖。

Busy's Diner - Intro

"Hello and welcome to Busy's, the home of the mediocre turkey club sandwich."

In this module’s long format problem, you will refactor the model of a diner. At this diner, there is only one dish on the menu: a mediocre turkey club sandwich. And since it’s Busy’s, there is also only one employee.

Open the Busy’sDiner project from the long-form-problem-starters repository, found on our [url=https://github.com/UBCx-Software-Construction-2/long-form-problem-starters]GitHub here[/url]. Expand the Busy’sDiner1 project. You will be working from Busy’sDiner1 for the first phase of refactoring. Take care not to expand Busy’sDiner2 or 3: they may give away parts of the solution!

First, navigate to the Diner class in the ui package. Run Diner.main() by right-clicking on the file in the sidebar and selecting ‘Run Diner.main()’ from the drop-down menu.

Inspect the output of main(). It looks like two tables were successfully served at Busy’s. Both tables managed to order, receive their food, and pay. Does that mean our project is following good design principles?

Take a look at the methods in Employee before moving on to the next page.

fitcat07 2017-11-11 13:41

Busy's Diner - Responsibilities

You probably noticed that Employee has two major responsibilities: serving a table, and preparing an order. In order to follow the Single Responsibility Principle, we need to make sure that Employee really has one central focus. Somewhat arbitrarily, we’ll decide that the single responsibility of Employee is to serve a table. Now we can refactor Employee by pulling out all kitchen responsibilities into a class named Chef.

Create a Chef class in the model package in src. Copy and paste all methods from Employee that are related to kitchen tasks into Chef (i.e. all methods that are not related to table service). You should also leave these methods in Employee for the time being; safe refactoring includes avoiding deletion until those methods are no longer being used by other classes.

The Chef class will not yet compile. Create the necessary constant PREFIX at the top of Chef, using the PREFIX defined in Employee as a guide. Create a constructor for Chef that takes no parameters. Declare a field of type Order, and set the field to null in the constructor. Assign the parameter order in makeDish to this.order. Lastly, include this.order = null at the very bottom of the plate() method. Re-run Diner.main() to ensure that the project still builds and runs correctly.

Next, we will reroute the calls in Diner. In Diner.main(), declare and initialize a Chef object below Employee. In the body of the for loop, call your Chef’s makeDish() method with the Order object o from employee.takeOrder().  Below the for loop, change the call to employee.doDishes() to chef.doDishes(). You should also delete the top 2 lines of Diner’s doOrderRoutine() method to ensure that the employee no longer cooks. You should now be able to delete the methods in Employee that belong in Chef.  

When you now run Diner.main(), you will notice that the Employee is only performing tasks related to table service, while the Chef is completing all the work in the kitchen. Excellent! That sounds like a much more efficient Diner.

Finally, to reinforce the central focus of the Employee class, let’s rename it to Server. Right-click on Employee in the sidebar and select Refactor -> Rename. Rename the class to Server (if you receive a warning that the Server class already exists in Busy'sDiner2 or Busy'sDiner3, just click continue). Ensure that all checkboxes are selected, then click Refactor. The next window will offer to rename the employee variable in Diner.main(); select the checkbox for the local variable in main() and click OK. You should also change the constant PREFIX in Server to reflect the name change. Lastly, re-run Diner to see the result of this change.

fitcat07 2017-11-11 13:42

Busy's Diner - Trendy Sandwich

For the next phase of refactoring, you will be working from the Busy’sDiner2 project, found in the same folder as Busy’sDiner1. This project is also solution to the Busy’sDiner1 refactoring you just completed.

Imagine that Busy’s Diner is getting an upgrade. Busy’s has decided to add avocado and sriracha sauce to their sandwich to make it “trendy” instead of “mediocre”. Let’s add those ingredients to the sandwich. Copy and paste the following line into the prepareIngredients method in Chef:

System.out.println(PREFIX + "Mashing avocado! Yum!");

Add the following line to followRecipe:

System.out.println(PREFIX + "Pouring sriracha! Spreading avocado! Trendy!");

Now run the Diner, and take a look at the output in order to answer the following question. Once you have answered this question, you may move onto the next page.

fitcat07 2017-11-11 13:43

Busy's Diner - Coupling

When we changed the ingredients of the mediocre sandwich, we introduced semantic coupling between Server and Chef. Of course, we could just manually change the describeDish method in Server to solve this one inconsistency. However, it’s likely that Busy’s will change something about their menu again in the future, so this is not a good solution. We need a better way to reduce coupling between Server and Chef.


One way to refactor semantic coupling is to introduce a new type of object to reduce duplication. Imagine if we had a Dish object associated with every Order. Each Dish could have its own ingredients list and recipe, which would be used by Server and Chef. This would reduce the “hard coding” of the recipe and ingredients that's causing the coupling.

Write down some fields and methods you may want in your Dish class before moving to the next page.

fitcat07 2017-11-11 13:43

Busy's Diner - Dish

Create a class Dish in the model package in src. Write two constructors: one should take only a String name, and the other should take a name, description, list of ingredients, and recipe.

Declare as many fields as you need to fulfill these constructors (note that you do not need to create any new classes to do this, it is fine for you to just use the primitive types String and List<String>). Remember to initialize all fields within both constructors; you may want to use the empty string and empty ArrayList to initialize some of the fields for the constructor that only takes a name as its argument. Generate getters and setters by right-clicking in the body of the class and selecting Generate -> getter and setter. Select all fields in the popup window, then click OK.

Now we have to consider which classes should use Dish. In the real model of a restaurant, we would likely have some type of Menu class to hold all the different types of dishes. At Busy’s, however, we only have one type of Dish, so we won’t create a Menu. Instead, we will have Server hold a field of type Dish, so the server can describe the sandwich before taking any orders.

Declare a Dish field inside Server, then add Dish as a parameter to the constructor, and initialize the Dish field inside the body of the constructor. (After all, we want servers to know about the dish as soon as they work in our restaurant!) You’ll notice that Diner no longer compiles. Declare our one Dish – the trendy turkey club sandwich – within Diner.main(), and pass it into the Server object’s constructor. You may want to copy and paste this helper method into Diner to simplify your main() method:

private static Dish generateTurkeyClubSandwich() {
        List<String> ingredients = new ArrayList<>();
        ingredients.add("avocado");
        ingredients.add("sriracha");
        ingredients.add("cheddar cheese");
        ingredients.add("bread");
        ingredients.add("lettuce");
        ingredients.add("tomato");
        ingredients.add("turkey");
        ingredients.add("bacon");
        return new Dish("Turkey club sandwich",
                "\"Our trendy sandwich has avocado, sriracha sauce, cheese, veggies, turkey and bacon.\"",
                ingredients,
                "\t1. Pour sriracha\n\t2. Spread avocado\n\t3. Stack meat\n\t4. Place veggies");
    }
Great! Now our Server knows everything it needs to know about a Dish, and we have all our sandwich information localized in a Dish object in main. Run Diner.main() to make sure the project still builds and compiles. The output is still the same, because we haven’t used the Dish in Server at all. We also haven’t added a Dish field in Chef. Hmm…what’s the best way to get a dish to a chef? Think about this before moving on to the next page.

fitcat07 2017-11-11 13:44

Busy's Diner - Using Dish

Although Busy’s only has one menu item right now, we probably want some flexibility to add more dish types in the future. If we attach a Dish to an Order, a Chef can follow the recipe for that order without having any other dependencies.

Notice that the Order class already has a field called dish, but the current type is String. Change the declaration of this type to Dish. This will cause several methods in Order to stop compiling; fix each error by changing the return and parameter types to Dish. You will also need to fix the compilation error in Server; to do so, you should pass the Server’s dish to the new Order object in takeOrder(). Run Diner.main() to ensure that the project still compiles and builds.

To simplify our method calls in Chef, let’s create a few more methods in Order to use the new dish field. Copy and paste the following stubs into Order, and complete the method bodies:

    //EFFECTS: returns the recipe to follow for this order
    public String getOrderRecipe() {
        return "";
    }

    //EFFECTS: returns a list of ingredients needed for this order
    public List<String> getOrderIngredients() {
        return null;
    }
Now we’re all set up to start using Dish in Chef and Server. We will need to generalize the output of Chef slightly to reduce potential semantic coupling. Refactor prepareIngredients() to use the list produced by this.order.getOrderIngredients(). You may wish to make use of a for loop and the System.out.print() method built into Java, which prints all items on one line until reaching a new line with “\n” or System.out.println(). You should also refactor followRecipe() to use this.order.getOrderRecipe().

Change the body of the Server’s describeDish() method to use the dish field's description. If you run Diner.main(), you will notice that almost everything looks perfect! The server’s introduction is now outdated, because we’ve changed something fundamental about the Busy’s Diner concept. We can’t quite solve that one with Dish, because our greeting message doesn’t necessarily have to depend on a dish at Busy’s. Instead, we’ll just change the word “mediocre” to “trendy”. Re-run Diner to make sure we’ve eliminated the semantic coupling.

fitcat07 2017-11-11 13:45

Busy's Diner - Host

Busy’s is expanding! The trendy sandwich is gaining popularity, so Busy’s is getting busier. To accommodate more customers, a new employee has been added to the mix. A Host greets customers, brings them to their table, describes the menu and delivers food. Hosts cannot take orders or payments.

Open Busy'sDiner3 and run Diner.main(). In the output, we can see Table 1 being served entirely by the server, and Table 2 being served with support from the host. Hmm…it looks like Table 2 encountered some errors! Open up the Host class to explore what could be going wrong. Once you have answered the questions below, you may move on to the next page.

fitcat07 2017-11-11 13:46

Busy's Diner - Liskov Substitution Principle

As you explored on the previous page, we can’t make Host extend Server because it violates the Liskov Substitution principle. Since Host can’t perform all of the behaviour of Server, it is weakening the postconditions of those methods; that is, it doesn’t adhere to all of the EFFECTS clauses specified in Server.

How do we fix this? An eager approach might be to have Host as a standalone class, and simply copy and paste the methods we need from Server into Host. However, this can cause duplication and semantic coupling, as we saw before with Server and Chef.

The next thing we might try is to create an interface. But we notice there is some shared implementation between Server and Host. Instead, what we probably want to do here is create an abstract class.

Let’s learn a bit more about refactoring before we make a big design change to the type hierarchies in our project.

fitcat07 2017-11-11 13:46

Busy's Diner - Refactoring

As discussed in the previous section, we want to solve the Busy's Diner substitution problem by creating a new abstract class. This is to encapsulate the shared implementation between Host and Server without violating LSP. We will need to change our project in several places: Server, Host, Diner, and a new class. We'll follow refactoring guidelines to make sure we're making changes safely and methodically.

Create an abstract class FOHEmployee in model. (FOH is short for Front of House – employees you interact with at a restaurant.) Which fields, constructors, and methods are shared between Host and Server? Pull any shared implementation into FOHEmployee. Take care not to delete anything from Host or Server - for now, we are only setting up the FOHEmployee class to be used by Server and Host.

One thing we notice about the shared implementation of deliverFood() is that it relies on a constant PREFIX. We can't simply define a PREFIX in FOHEmployee, because its value should differ depending on the specific subtype. How can we allow for some subtype variation to ensure that the deliverFood() method prints out the correct prefix?

Define a new method in FOHEmployee: public abstract String getPrefix(); . Call getPrefix() in place of PREFIX in deliverFood(). FOHEmployee should now compile. Even though we still haven't used FOHEmployee anywhere in our program, we still want to run Diner.main() to make sure we haven't broken anything before we proceed.

fitcat07 2017-11-11 13:48

Busy's Diner - Using FOHEmployee

Next, we will modify the Server and Host classes to extend FOHEmployee. Modify the Host class declaration to extend FOHEmployee instead of Server. IntelliJ will underline the class declaration in red: this is because Host needs to implement the abstract method getPrefix(). Define this function in Host to use the PREFIX constant at the top of the class. Do not remove any methods from Host yet. (You will need to remove any @Override annotations for the class to compile.)

Now modify the class declaration of Server to extend FOHEmployee. Implement getPrefix() in Server. This class still does not compile; that's because the first line in the Server constructor must be a call to the super constructor. Fix this error by calling super with the appropriate parameter. Do not change the rest of the constructor.

The next step in refactoring is to make sure our code still runs before we start deleting methods from Host and Server. Refactor Diner to change the apparent type of host to Host. This will "break" the call doOrderRoutine(host, o2). Refactor doOrderRoutine to work with FOHEmployees instead of Servers only. Our solution defines two new methods: public static void doFOHOrderRoutine() and public static void doServerOrderRoutine().

Now we can start removing duplication from Host, Server and FOHEmployee. In Server, delete all methods that appear in FOHEmployee, and delete the field dish. If Server isn't compiling, ensure that the dish field in FOHEmployee is protected, not private. Re-run Diner.main(). Your project should still build and compile with the same output.

In Host, delete all methods except getPrefix(). Since all shared functionality between Host and Server is now in FOHEmployee, this should be a safe operation. However, we notice that Diner has stopped compiling. This is because we call host.takeOrder() in Diner.main(). That makes sense - we started refactoring because we don't want hosts to take orders. Now, IntelliJ is notifying us about errors that we want to catch, instead of displaying errors at runtime. That's great! It means we have eliminated the substitution problem.

Once your project compiles and runs, with Host and Server extending FOHEmployee and no duplication among the 3 classes, you have fully completed your refactoring. Congratulations!

If you like, you can compare your solution to ours [url=https://github.com/UBCx-Software-Construction-2/long-form-problem-solutions]here[/url]. Our solution includes an additional abstract method  getShortPrefix(), which is added to the beginning of all methods in FOHEmployee. This is included simply for clarity to see which employee completes which task. Feel free to implement this addition yourself for extra practice.

"Thanks for visiting Busy's Diner!"

fitcat07 2017-11-11 13:52

題目太長,相信睇嘅人唔多。
但係,我諗OOD高手,唔須要睇題目,單看源碼,應可以睇到課堂答案嘅問題。

Susan﹏汪汪 2017-11-11 14:01

[quote]原帖由 [i]fitcat07[/i] 於 2017-11-11 01:52 PM 發表 [url=http://computer.discuss.com.hk/redirect.php?goto=findpost&pid=470714048&ptid=27050301][img]http://computer.discuss.com.hk/images/common/back.gif[/img][/url]
題目太長,相信睇嘅人唔多。
但係,我諗OOD高手,唔須要睇題目,單看源碼,應可以睇到課堂答案嘅問題。 [/quote]
唔關係咪高手事
如果係設計得好的話

大多數人應該就咁睇個main都知發生咩事
因為好的OOD係要求多數人唔需要睇internal implementation都能理解段code做緊乜

所以如果睇返呢個
[url]https://github.com/UBCx-Software-Construction-2/long-form-problem-starters/blob/master/Busy'sDiner/Busy'sDiner1/src/ui/Diner.java[/url]

如果單睇呢到都知發生咩事的話
咁個design就係成功

Susan﹏汪汪 2017-11-11 14:06

[code]public static void main(String[] args) {
        Employee employee = new Employee();

        for (int i=0; i < 2 ; i++) {
            System.out.println("Table " + (i + 1) + ":\n");

            employee.greet();
            employee.describeDish();
            Order o = employee.takeOrder();

            System.out.println();

            doOrderRoutine(employee, o);
            System.out.println();
        }

        System.out.println();
        employee.doDishes();
    }[/code]汪汪就咁睇呢到

可以知道個employee有個工作流程
1. 打招呼(greet)
2. describeDish, 呢到唔清楚點解employee要去describe dish
可能主客掉轉左
3. 然後攞到一張單(takeOrder)
4. 落單(doOrderRoutine)

fitcat07 2017-11-11 15:24

[quote]原帖由 [i]Susan﹏汪汪[/i] 於 2017-11-11 02:01 PM 發表 [url=http://computer.discuss.com.hk/redirect.php?goto=findpost&pid=470714418&ptid=27050301][img]http://computer.discuss.com.hk/images/common/back.gif[/img][/url]
唔關係咪高手事
如果係設計得好的話

大多數人應該就咁睇個main都知發生咩事
因為好的OOD係要求多數人唔需要睇internal implementation都能理解段code做緊乜
[/quote]
MOOC課堂嘅唔係「大多數人」?佢哋係「少數人」?
唔係高手,可以指出有冇違反SOLID principles?個設計係咪high cohesive, low coupling?單睇main就可以?
我諗你想得太間單了。

Susan﹏汪汪 2017-11-11 15:45

[quote]原帖由 [i]fitcat07[/i] 於 2017-11-11 03:24 PM 發表 [url=http://www.discuss.com.hk/redirect.php?goto=findpost&pid=470718032&ptid=27050301][img]http://www.discuss.com.hk/images/common/back.gif[/img][/url]

MOOC課堂嘅唔係「大多數人」?佢哋係「少數人」?
唔係高手,可以指出有冇違反SOLID principles?個設計係咪high cohesive, low coupling?單睇main就可以?
我諗你想得太間單了。 [/quote]
首先要知道

如果其他人睇唔明你的code
講乜design pattern都冇用

如果個design pattern係只有精英先能夠處理的話
咁本身就已經係失敗

Susan﹏汪汪 2017-11-11 16:16

其實要理解OO一D都唔難
從來都係睇需求做主導

佢個Busy's Diner本身條題目出得唔好

用另一個例子做說明
假設而家implement 一個遊戲

如果要控制個角色做移動的話
可能係一個人

果個人應該係有一連串動作
提腳、踏前、再提另一隻腳、踏前

不過好少少嘅OO設計
就應該要把呢個角色包裝成一個Character object
然後比個指令個character 叫佢郁

Character 裡面implement 左移動的一連串動作
外面唔需要知裡面點implement

情況同個Busy's Diner一樣
根本唔需要了解employee入面係點
就可以了解個遊戲點運作

再進一步講
如果汪汪要再寫更豐富的功能
就咁叫個character 移動係不能滿足需求

可能再寫個AI模組
個AI模組會利用地形資料、同埋character 的移動指令
可以組合出一個新指令:移動至目標位置

咁汪汪就可以直接指示個史萊姆追住個主角跳
唔需要考慮個AI點計劃路徑

Susan﹏汪汪 2017-11-11 16:21

[quote]原帖由 [i]Susan﹏汪汪[/i] 於 2017-11-11 04:16 PM 發表 [url=http://www.discuss.com.hk/redirect.php?goto=findpost&pid=470720193&ptid=27050301][img]http://www.discuss.com.hk/images/common/back.gif[/img][/url]
其實要理解OO一D都唔難
從來都係睇需求做主導

佢個Busy's Diner本身條題目出得唔好

用另一個例子做說明
假設而家implement 一個遊戲

如果要控制個角色做移動的話
可能係一個人

果個人應該係有一連串動作
提腳、踏 ... [/quote]
由呢個例子可以見到

個核心只有「移動」呢個method
呢個係抽象

史萊姆呢個character 負責implement 呢個method
AI模組就加以利用、可以做更多嘅變化

即係話呢到所有野都係依賴「移動」呢個抽象

也當然
AI模組本身又係另一個抽象
裡面點implement 也都係呢個模組自身問題

[[i] 本帖最後由 Susan﹏汪汪 於 2017-11-11 04:30 PM 編輯 [/i]]

Susan﹏汪汪 2017-11-11 16:43

[quote]原帖由 [i]Susan﹏汪汪[/i] 於 2017-11-11 04:21 PM 發表 [url=http://www.discuss.com.hk/redirect.php?goto=findpost&pid=470720436&ptid=27050301][img]http://www.discuss.com.hk/images/common/back.gif[/img][/url]

由呢個例子可以見到

個核心只有「移動」呢個method
呢個係抽象

史萊姆呢個character 負責implement 呢個method
AI模組就加以利用、可以做更多嘅變化

即係話呢到所有野都係依賴「移動」呢個抽象

也當然
AI ... [/quote]
如果要再把AI模組object 化

汪汪可能會寫個Brain去代表AI功能
然後再給character 塞個brain 呢個object

結果可以係不同生物都有各自的智力水平
可能史萊母只係識直接追主角
其他生物就識走迷宮

甚至個brain object有各自的記憶功能
可以記住走過的地方、知道見過的地方的地形

Susan﹏汪汪 2017-11-11 16:59

講返個Busy's Diner

需求做主導去諗
汪汪留意到題目可能話describe dish設計得唔好

所以就寫一堆insertion 出來
去砌個menu

汪汪諗得更深入的話
會問點解個menu係一堆insertion

就應該要諗menu本應係由Restaurant提供
Employee只係describe由restaurant 提供的menu

呢到已經出現左restaurant 呢個概念
至於應唔應該把restaurant 寫成object?
可能變左容許多間restaurant
唔同restaurant 都提供不同menu

fitcat07 2017-11-11 18:20

[quote]原帖由 [i]Susan﹏汪汪[/i] 於 2017-11-11 03:45 PM 發表 [url=http://computer.discuss.com.hk/redirect.php?goto=findpost&pid=470718903&ptid=27050301][img]http://computer.discuss.com.hk/images/common/back.gif[/img][/url]

首先要知道

如果其他人睇唔明你的code
講乜design pattern都冇用
[quote]
同意,但離題。此帖唔係講可讀性(雖則好設計亦應有可讀性),係講設計。
你句說話可以成萬能key。
廣告:
如果其他人睇唔明你的內容
講乜sound bite都冇用
文章:
如果其他人睇唔明你的句子
講乜修辭手法都冇用
...
[quote]
如果個design pattern係只有精英先能夠處理的話
咁本身就已經係失敗 [/quote]
唔好又改變帖子內容,我係講OOD高手,唔係精英!
我自己嘅定義:
精英 = 少於5%最叻嘅人
大師 = 其次10%
高手 = 其次15%
及格 = 其次20%
平庸 = 其次40%
低下 = 餘下10%

>=高手,有30%。

重有,我冇講過要「處理」。我講嘅係:「但係,我諗OOD高手,唔須要睇題目,單看源碼,應可以睇到課堂答案嘅問題。」係睇出有冇問題,並無包含「處理」。有尐好困難嘅情況,的確要有「精英」才能「處理」好。

最後,此帖唔係講design pattern,而係design principles,兩樣嘢好唔同。

fitcat07 2017-11-11 18:26

[quote]原帖由 [i]Susan﹏汪汪[/i] 於 2017-11-11 04:59 PM 發表 [url=http://computer.discuss.com.hk/redirect.php?goto=findpost&pid=470722231&ptid=27050301][img]http://computer.discuss.com.hk/images/common/back.gif[/img][/url]
講返個Busy's Diner

需求做主導去諗
汪汪留意到題目可能話describe dish設計得唔好

所以就寫一堆insertion 出來
去砌個menu

汪汪諗得更深入的話
會問點解個menu係一堆insertion

就應該要諗menu本應係由Restauran ... [/quote]
唔好離題,題目已有講要求,係一個簡化版,並唔係現實情況。

此帖係講,究竟答案係咪好嘅OOD?如果係,請指出好嘅地方,以便討論。如否,即MOOC課堂亦產生唔好嘅OOD,回應主題OOD係難,所以出題者也做得唔好。

Susan﹏汪汪 2017-11-11 18:43

[quote]原帖由 [i]fitcat07[/i] 於 2017-11-11 06:26 PM 發表 [url=http://www.discuss.com.hk/redirect.php?goto=findpost&pid=470726073&ptid=27050301][img]http://www.discuss.com.hk/images/common/back.gif[/img][/url]

唔好離題,題目已有講要求,係一個簡化版,並唔係現實情況。

此帖係講,究竟答案係咪好嘅OOD?如果係,請指出好嘅地方,以便討論。如否,即MOOC課堂亦產生唔好嘅OOD,回應主題OOD係難,所以出題者也做得唔好。 [/quote]
佢題目設計得唔好並唔係因為主題OOD難

而係佢用一個本身已經OO的題目
叫學生寫個更OO的code

呢個係個題目出得唔好的原因
也都係題目點解困難的地方

就好似教人點優化sorting algorithm
個老師一開波就寫個quick sort出黎叫學生寫個更快嘅一樣道理

fitcat07 2017-11-11 18:48

[quote]原帖由 [i]assembly.jc[/i] 於 2017-11-11 11:28 AM 發表 [url=http://computer.discuss.com.hk/redirect.php?goto=findpost&pid=470708017&ptid=27050301][img]http://computer.discuss.com.hk/images/common/back.gif[/img][/url]
是 OOD 因難,還是設計大型系統因難,還是用 OOD 去 設計大型系統因難?
[/quote]
正因設計大型系統困難,良好嘅OOD先重要。
數百行嘅程式,無必要好嘅OOD。
數千行但唔會改變或維護嘅系統,亦無必要有好嘅OOD。
萬行以上或要求經常改變嘅系統,良好嘅ODD係必要。[quote]
OOD 可能唔難,但用 OOD 去解決上述問題時,就變得難,部份原因可能是半數問題不是 design 問題,而是管理問題,Database 問題,security 問題等等。
另外,實務上,著重「變」,換成 design 的術語就是 "Coupling"。如果改小部 requirement 就要推倒整個設計,咁不如沒有設計。
最後,是尋找問題的能力,大型系統,source code 動輒數十萬行,有問題時去 source code 找,去看 documentation? OOD 有方法能快速指出問題所在? 還是靠 unit test, regression test。[/quote]
我嘅問題應該係:「要做到[b]良好[/b]嘅OOD難唔難?」可能我一向以為唔須要寫白[b]良好[/b]兩字,因而產生誤會。話時話,唔好嘅OOD,唔會難呱?

如有良好嘅OOD,應可解計好多設計上嘅問題,亦可輕鬆應付要求改變。當然,OOD唔係萬靈丹,唔可以解決一切問題。管理上嘅問題,如budget、人手,OOD解決唔到,亦唔係此帖所關心嘅事。

fitcat07 2017-11-11 19:00

[quote]原帖由 [i]Susan﹏汪汪[/i] 於 2017-11-11 06:43 PM 發表 [url=http://computer.discuss.com.hk/redirect.php?goto=findpost&pid=470726855&ptid=27050301][img]http://computer.discuss.com.hk/images/common/back.gif[/img][/url]
佢題目設計得唔好並唔係因為主題OOD難

而係佢用一個本身已經OO的題目
叫學生寫個更OO的code

呢個係個題目出得唔好的原因
也都係題目點解困難的地方
[/quote]
題目有講已經做咗個OOD,當然係唔好果種。題目要求就係要改良現時設計,使到成為一個更好嘅OOD。[quote]
就好似教人點優化sorting algorithm
個老師一開波就寫個quick sort出黎叫學生寫個更快嘅一樣道理[/quote]
例子錯了。以下才是對應例子:
老師教如何利用重構,使程式變得易讀及易於維護。
老師寫咗個算法程式碼,執行無誤,但就用咗好多唔好嘅寫法,如所有變數只得一個字母、程式只得一個超過200行嘅函數、好多重複碼等。現要求學生將之重構。

Susan﹏汪汪 2017-11-11 19:14

[quote]原帖由 [i]fitcat07[/i] 於 2017-11-11 07:00 PM 發表 [url=http://www.discuss.com.hk/redirect.php?goto=findpost&pid=470727525&ptid=27050301][img]http://www.discuss.com.hk/images/common/back.gif[/img][/url]

題目有講已經做咗個OOD,當然係唔好果種。題目要求就係要改良現時設計,使到成為一個更好嘅OOD。
例子錯了。以下才是對應例子:
老師教如何利用重構,使程式變得易讀及易於維護。
老師寫咗個算法程 ... [/quote]
咁汪汪前面個restaurant 已經係例子

佢有講到話describe dish唔夠彈性
所以寫左一個function 出來就咁一堆add()

汪汪就係講佢呢個唔係OOD
佢只不過係寫左一個function return 一個array

不是抽象依賴
頁: [1] 2 3 4 5 6 7
查看完整版本: OOD 難唔難?