The average web-based shop works like this: there’s a front and a back. The front is sexy, the back is not. One group of people goes to the front to buy stuff, other group goes to the back to manage that stuff. These groups of people intersect only when those who go to the back pretend they are buyers. But still, there’s a huge distinction.
On the other hand, the real shop also has a front and a back. But those who sell stuff do not stay in warehouse all day long turning knobs to rearrange stuff on shelves. They actually go to the shop room to put things in place, see how they look and how customers explore them. In other words, a physical shop is more WYSIWYG than a web-based one.
My suggestion is to outsource to warehouse pages as little functions as possible. The shop owner should have a control on what is presented to the people. He should be able to immediately update prices, titles, rearrange things and see some basic stats which help to decide how good something sells.
I’m using Safari because it is fast, simple and not ugly. But it has several annoying issues:
1. It does not return allocated memory back to system (however, it recycles it internally and does not leak). I end up with Safari eating 1+ gigs and multiple windows with multiple tabs opened. And it is not easy to restart Safari due to a second issue:
2. It does not remember opened windows and tabs automatically. If I opened 10 tabs to read later (or to work with), I have to save them in a bookmark group. And it is not easy to remove/add new pages to that bookmark group.
3. Even if I have enough memory and Safari does not crash too often, I still have multiple windows and tabs, and hidden tabs. It is hard to switch between them, especially using a keyboard.
Let’s see what we can do about it.
First, I turn to other applications I use every day: Mail, TextMate and Xcode.
In Xcode and TextMate I have a quick access to any file using a Cmd+T shortcut: I press Cmd+T, a small window pops up with a search field. I type a portion of file name and it appears in the list. Press Enter — and here it is.
Cmd+T is useful for rare switching. For more frequent switches there’s Alt+Cmd+Up to switch between .h/.m files and Alt+Cmd+Left/Right to navigate through tabs in TextMate.
In Mail I have an Inbox and the folders. Inbox contains unread/undone messages. Everything else is in the folders.
In Mail I also have a single window with a single list of items. If I want to open a particular message in another window, I double-click on an item.
With some background I may draw some requirements for a browser useful at work:
1. It should always remember all windows positions and opened websites. If it crashes, it should restore all pages as they were opened before crash. (Just like Opera did for many years, by the way.)
2. It should have a Cmd+T menu to have a quick keyboard access to all open and bookmarked pages.
3. It should remember history for a page even if it is opened in a new window or tab.
4. The unread pages should be marked so.
5. There should be a context menu item “Read Later” which puts the link into Inbox and marks it as unread.
6. It should cache all the bookmarked pages on disk (in a form of webarchive) for indefinite amount of time. Each new version should be stored as well, but not overwrite the previous one. I want to access exactly the same content I’ve once seen.
7. Closed pages should be easy to reopen. (Safari lets you reopen a window, but not a tab).
Next step is to make some UI mockups to see how silly (or not) these desires are.
In the first part I suggested to use messages like to_my_interface or asMyInterface to ensure interface-compliance. This method just returns whatever object seems to be suitable for the interface, but does no actual checks. IDE like Xcode or Eclipse can do a static check for implemented methods and issue warning/error at compile time. Today I will show how to build the same feature in the dynamic system (and make it more useful).
First, two important points from the first part:
One object (sender) send a message to another (receiver) with an argument which should implement String interface (receiver will call asString method on its argument).
1. The sender does not need to perform any “casting” before sending an interface-compliant object. If appendString(obj) wants a string and you know that your object can (or should) behave like a string, you just pass it in without any conversion. Only low-level method which relies on actual string methods like substring should send asString to obtain the String interface.
2. The receiver should not perform any checks like respondsToSelector or isKindOfClass. It should only tell the argument to return something with an interface it needs.
If both sender and receiver do not check actual interface compliance, where should it happen? Obviously, inside the asString method:
# this method does not check the interface
def asString
self
end
# this method checks the interface
def asString
String.check(self)
end
How’s that different from just testing application as before? First of all, check is performed before any protocol message is send to the subject. So it is more like a “compile-time” check: some message could not be sent in runtime, but the error could be thrown before.
In addition to basic checks like missing methods String.check can also perform some unit tests (and be smart enough to test each particular class (hidden class) only once).
Needless to say that interface checks could be omitted in production mode to increase performance.
Tages Anzeiger redesign suggestion.