tag:blogger.com,1999:blog-74510860061685699932024-03-14T02:54:19.879-04:00Arbitrary ThoughtsArbitrary thoughts on java, programming and other misc things.Andrewhttp://www.blogger.com/profile/17391659235860245424noreply@blogger.comBlogger142125tag:blogger.com,1999:blog-7451086006168569993.post-4377994655135724012023-03-27T16:20:00.003-04:002023-03-27T16:20:48.522-04:00Rust<p>I've just recently completed an online course for Rust. I find Rust a very interesting language. I've always subscribed to the philosophy that the compiler should try as hard as possible to find errors for you. While I can appreciate an interpreted language like Python, in my experience Python requires an enormous amount of discipline to write a large Python program with many team members. In a language like Java, the compiler is doing so much tedious work by making sure you don't pass arguments or assign variables of the wrong type.</p><p>The interesting thing about Rust is that it doesn't have a garbage collector. Rust is a replacement for system languages like C or C++, where it is not always possible or desirable to have a garbage collector thread working away int he background.</p><p>While I'm not the best at C++, I have used it enough to realize that managing memory consumes a big chunk of mental overhead. I know about patterns like using the stack to free resources and I know all about memory pools, but I don't feel comfortable enough with C++ to say that I've fully assimilated all the patterns good C++ programmers use to keep themselves out of trouble. Rust is a window on that world.</p><p>Whenever you find people using a (good) pattern, other people will try to make a computer language that accommodates it. Using a stack while in assembly leads to things like procedures. The judicious use of goto leads to things like the break or return statement. Even recently, a style of programming that tries to make everything immutable leads to a type of programming called functional programing. Rust tells me about the types of patterns C++ programmers use to keep themselves from making mistakes.<br /></p><p>Rust has this thing called the borrow checker. It's primary purpose it to make sure that only one chunk of code owns a variable or piece of memory. Without a garbage collector, every object must be manually freed exactly once. This functionally means that every object has an owner who's job it is to manage the lifetime of the object and free it when it's not used anymore.</p><p>The borrow checker essentially forces you to make clear which functions are merely using an object and which function owns the object. It works surprisingly well although I've heard complaints that for things like a tree structure with a parent back links you have to do some serious thinking to make it all work. Honestly, I remember writing a doubly linked list in C++ and felt it was like watching <a href="https://www.youtube.com/watch?v=v4nLrDtwBcs">bender putting on his own arms</a>.</p><p>In any case, I hope I have the privilege of working on a Rust code base at some point. At the very least it will make me a better C++ programmer.<br /></p>Andrewhttp://www.blogger.com/profile/17391659235860245424noreply@blogger.com0tag:blogger.com,1999:blog-7451086006168569993.post-57078919889747164702023-02-22T16:31:00.003-05:002023-02-22T16:31:55.075-05:00Gave up on using a Ryzen 9 5950X with a B450 MSI Tomahawk motherboard - too unstable<p> So after much time and effort trying to stabilize the Ryzen 9 5950X with the B450 Tomahawk, I gave up and just got a B550 Tomahawk motherboard. All the things that should have worked on the B450 motherboard now work on the B550 motherboard. Specifically, you can run it at default "auto" BPO settings. You can run it with PBO enabled. You can run the ram at the XMP-1 and XMP-2 profiles (3200Mhz). I can even max out the system's cooling at hit 90C on the processor. Nice.</p><p>So what went wrong with the B450? No idea. It's probably power/VRM related but it could also easily be electrical interference related as well. All I know is it it kept giving me the same error: black screen and it needed to be reset at the power supply switch because holding down the power on button didn't work.</p><p>Fun facts, if you run the 5950X with PBO explicitly disabled AND you set the ram to 2133Mhz, it runs reasonably stable but will sometimes crash after a sleep. Increasing the RAM bus speed or turning on PBO will make it more likely to crash. Weirdly setting the PBO power limits to the motherboard limits didn't seem to hurt stability that much. In fact there's a chance the stability was a little better. It makes intuitive sense at first glance but AFAIK the motherboard power limits are higher than if you simply disable PBO.. and setting higher limits should cause more instability.. so I'm a bit suspicious of my subjective stability experience.<br /></p><p>Another interesting factoid involves the RAM compatibility list that MSI produces for that motherboard. Initially they didn't differentiate between the 5000 series and the rest but since then they've revised it so that the 5000 series had a different list with no ram listed as being compatible over 2666Mhz. Recently I can't even get MSI's website to produce a ram compatibility list for the 5000 series processors. I'm guessing this means MSI has noticed the board is flaky with 5000 series processors.</p><p>I'm putting this out there to save people a bit of time if they're having trouble with 5000 series Ryzen processors on the B450 Tomahawk board. You're not going to find stability nirvana with that board. <br /></p>Andrewhttp://www.blogger.com/profile/17391659235860245424noreply@blogger.com0tag:blogger.com,1999:blog-7451086006168569993.post-90758187398606578572022-12-28T20:30:00.004-05:002022-12-28T20:33:25.565-05:005950X B405 MSI Tomahawk crash caused by XMP?<p>Ok guys. Just a quick post here...</p><p>so I recently bought a 5950X for use on my B450 MSI Tomahawk motherboard. I figured everything should work ok. However, I've had multiple problems.</p><p>First problem was that the motherboard's default PBO setting (or "auto") seems to cause the machine to crash. I don't really know why since it should BE OFF BY DEFAULT but whatever. If you set the setting to disabled or configure PBO to use the motherboard defined power limits you'll be good to go.I don't really understand why this is. The motherboard is capable of supplying more power than the CPU needs but if you turn it on, or just leave it as "auto", the machine is unstable. Using motherboard power limits (which are very high) seems to work fine though. WTF? <br /></p><p>The second more surprising problem is that enabling XMP with my super low latency memory causes random (if somewhat infrequent) crashes. What happens is the screen goes blank and the system won't respond to any attempts to reset. You have to turn off the power supply, wait a few seconds, then turn it back on. It's a nasty crash. This is also the symptom if PBO is set to auto, btw. Initially I thought this was power related since I was using a cheap 550W power supply. But upgrading to a 750W good power supply did nothing.</p><p>In any case the MSI Tomahawk website's supported memory list gives a clue as to what is happening here. The motherboard supports different memory depending on the generation. The Ryzen 5000 series only supports memory up to 2666Mhz speeds.. Which is weird.. but is a big clue that there's some issue with the motherbaord, the 5000 series and high speed memory.</p><p>In any case I'm now running my memory at 2666Mhz but with crazy low timings of 12-12-12-28 thanks to having 3200Mhz memory with 14-14-14-34 timings running at a lower bus speed. Memory timings are specified in clock cycles but the timings don't change with clock speed so the numbers can be brought down when the bus speed decreases. So far it's stable and it's been a while.</p><p>I'm putting this out there because it took a stupidly long amount of time to figure this out so hopefully it will show up if someone searches for it. The forums have some of this info but the forums also have a bad signal to noise ratio.<br /></p><p><br /></p>Andrewhttp://www.blogger.com/profile/17391659235860245424noreply@blogger.com0tag:blogger.com,1999:blog-7451086006168569993.post-84966384783886701652017-12-20T20:49:00.000-05:002017-12-20T20:49:33.678-05:00Quebec Monorail (moteur roue - moh tar rooo)Philippe Couillard has proposed building a monorail from Montreal to Quebec to deal with traffic issues between the two cities. The proposal is to use <a href="http://www.trensquebec.qc.ca/english">Trensquebec's</a> theoretical monorail idea to create a 250km/h overhead style monorail. I'm all for high speed rail links between cities, what puzzles me is how a crackpot idea got enough traction to end up being proposed by the premier of Quebec.<br />
<br />
Many people seem to think of convention rail as being very slow. It certainly doesn't help that most of the experiences we have with rail involved waiting at rail crossings in our cars while an incredibly slow freight train passes by. The definition of conventional rails goes up to 200km/h for pre-exisitng track and 240km/h for new track. High speed rail is typically 300km/h. Even VIA rail trains, that aren't known for their speed, are going 150km/h. It's quite humbling when you're passed by a VIA train while driving along the 401.<br />
<br />
The biggest problems with going fast on existing rails in North America is the entire system is setup and optimized for freight. The track quality is not great, the signaling is all setup for low speeds, the bends are tight, there's level crossings all over the place and the mile long freight trains inch by at 50km/h. There are so many freight trains that VIA will be unable to provide service in the near future as it will be too crowded to run passenger trains. All this is why VIA trains go at 150km/h instead of 200km/h.<br />
<br />
The solution is new track. VIA has proposed a very inexpensive solution they call <a href="http://www.viarail.ca/en/about-via-rail/governance-and-reports/dedicated-tracks">high frequency </a>rail. The proposal creates a VIA dedicated line so that they can run their trains at 150km/h to +180km/h speeds with no random delays (like today) thanks to freight trains blocking the way. What people have noticed in europe is that the frequency of the trains and sticking to a schedule is more important to ridership than max speed. With VIA's proposal it gets that.<br />
<br />
Then there's Trensquebec. The biggest issue with them is there's no technology. It's not a real project. There's not even a prototype train on prototype track. There's also their suspiciously low cost estimates (it should be higher than the high speed rail proposals) as well as their suspiciously high operating speed (I would estimate ~200km/h max if that). At this point I think it's a big con. Quebec should be teaming up with the federal government and private industry to back VIA's proposal.<br />
<br />
<br />
<br />Andrewhttp://www.blogger.com/profile/17391659235860245424noreply@blogger.com0tag:blogger.com,1999:blog-7451086006168569993.post-53213246948752357682017-11-29T21:23:00.000-05:002018-04-06T15:33:16.194-04:00Microsoft EdgeMozilla has recently release <a href="https://www.mozilla.org/en-US/firefox/">Firefox Quantum</a>. I supposed because it's supposed to be a quantum leap in web browser technology. Does this means that Scott Bakula will show up in your web browser at critical moments to help guide you through your web browsing experience? Nope. Sorry. It just means the Firefox has a brand new web engine that's been redesigned to similar to Chrome but better. If you haven't been taking Firefox seriously for a while now is the time to try it again. My experience has been great so far. Not only have I started to use Firefox again but it's now my web browser of choice.<br />
<br />
Recently Windows went through one of those comically long updates and loaded the release notes for the update into Microsoft Edge web browser. Neat! I thought to myself. I haven't tried Microsoft's web browser in a long time and I know they've been working on it. If Firefox can improve so much maybe Edge can as well.<br />
<br />
The first thing I noticed in Edge is that the scroll wheel on my mouse doesn't work. It seems to work everywhere else just not in Edge. How did they even do that? Well, I'm not going to debug that. Not now I have a Firefox that doesn't suck. Maybe I'll try Edge again in another 5 years or so. You know, when they've figured out how to get the mouse wheel scrolling work.<br />
<br />
<i>New information! It turns out that CatMouse was causing the problem. Windows 10 seems to ahev CatMouse like functionality built into it so removing it was now big deal.</i>Andrewhttp://www.blogger.com/profile/17391659235860245424noreply@blogger.com0tag:blogger.com,1999:blog-7451086006168569993.post-85966210281577143692017-11-23T18:11:00.001-05:002017-11-23T18:11:34.026-05:00IPv6 adoption hits 20%About 2 years ago I noticed that IPv6 adoption was at about 7%. Well now <a href="https://www.google.com/intl/en/ipv6/statistics.html#tab=ipv6-adoption">IPv6 adoption is at 20%</a> and still growing quickly. 20% is excellent! One in five people on the internet have an IPv6 address. This is the year of IPv6.<br />
<br />
I'm still a little hopeful that this will mean that p2p technology will become viable again, but it's hard to say for sure. Security is much more important than it once was. I've also been burnt by the criminally cavalier approach to security that most companies seem to have. I may trust my router (barely) but not anything else. As a result I haven't turned on IPv6 on my router because <a href="https://arstechnica.com/information-technology/2016/01/how-to-search-the-internet-of-things-for-photos-of-sleeping-babies/">I don't trust that everything on my LAN</a> will actually do the right thing in that scenario.<br />
<br />
*sigh*<br />
<br />
Security on desktop and laptop machines isn't too bad anymore. The Windows 10 and OS X will ask the user if a program wants to open a port. Additionally, modern OSes do try and minimize their attack surfaces by not listening for connections when not required. I just can't remember if I've done something silly like turned on file sharing with guest access and that will be shared on the open internet. It wouldn't be the first time I've made that mistake. One of these days I'll have the time to experiment and see if file sharing restricts itself to LAN addresses. Also, if we get an public IPv6, does the OS also give us a private LAN address as well so we can bind more sensitive services only to that.Andrewhttp://www.blogger.com/profile/17391659235860245424noreply@blogger.com0tag:blogger.com,1999:blog-7451086006168569993.post-16497571575084990972017-11-21T18:33:00.003-05:002017-11-21T18:33:44.101-05:00Typescript is funI recently went from programming on Java for almost 20 years to programming exclusively in Typescript and I like it.<br />
<br />
Typescript is a super set of Javascript. It is essentially a typing system built on top of Javascript and it works amazingly well. When I first started programming in it I expect it to be a dancing bear, and from one perspective it sort of is, but man does this bear dance.<br />
<br />
The way to think about Typescript is it makes Javascript scale. It's not really trying to be its own language as much as a set of tools that allow you to take existing Javascript code and add some typing sanity onto it. What makes it work is a combination of crazy good type inference and a sort of strong duck typing. So if you have something like this:<br />
<br />
interface Person {<br />
String name;<br />
int age;<br />
}<br />
<br />
interface Monkey {<br />
String name;<br />
int age;<br />
} <br />
<br />
<br />
you can do this:<br />
Person p = new MonkeyInstance();<br />
<br />
or this:<br />
<br />
Person p = { name: "Steve", age: 12 };<br />
Monkey m = p; <br />
<br />
and it doesn't care. So long as the interfaces are compatible you can assign it and it's all good. Java can't do this because Java is tracking types at the byte code level. A monkey and person are two different types that just happen to have the same properties. For Typescript having the same properties is what defines a type.<br />
<br />
(Java does allow this sort of assignment in an around about fashion with functions. For instance, you can pass any method that takes a value and returns a value to a method expecting a Function<f>. But it only works with functions, not objects)</f><br />
<br />
Typescript also implements an idea I've wanted to see implemented for a long time. Nullable as a type!<br />
<br />
In Java, we've started to use "Optional" instead of null because it makes the optional nature of the value part of the type. But, what if the language did it for you? Well Typescript does. Basically, you can't do this:<br />
<br />
Person p = null;<br />
<br />
You have to express that p might be null like this:<br />
<br />
Person | null p = null;<br />
<br />
Trying to do this using Java syntax gets confusing but the idea is that p might be a Person or it might be a null. Like using Optional, it makes the compiler track that a value might be optional, but unlike optional it's built in. The same multi-type trick works with any types:<br />
<br />
Person | Exception p = findPerson();<br />
<br />
p might be an Exception type or a Person and you'd need to do some additional work to work out what p is. <br />
<br />
You can append however many you want:<br />
<br />
Person | Exception | null p = findPerson();<br />
<br />
In java you can do this with exception catch blocks, but nowhere else:<br />
<br />
try {<br />
stuff();<br />
} catch (NullPointerException | IOExecption | SqlException ex) {<br />
// ...<br />
}<br />
<br />
Here's a <a href="https://www.youtube.com/watch?v=f6TCB61fDwY">video of the inventor of Typescript explaining how Typescript deals with null</a>.<br />
<br />
<br />Andrewhttp://www.blogger.com/profile/17391659235860245424noreply@blogger.com2tag:blogger.com,1999:blog-7451086006168569993.post-76713036579523699522015-06-07T10:56:00.003-04:002015-06-07T10:56:29.392-04:00ipv6 is almost hereA few months ago we ran out of new IPv4 block to assign. That basically means we're out of IPv4 addresses. We need more! MOAR!!!! In order to get more (or moar) we need to move to IPv6 which is the next generation internet protocol (IPv5 was skipped because it opened a rift in space time and caused Vint Cerf to lose all his hair so they never deployed it). The trouble with IPv6 is that it's not compatible with IPv4. You can run both IPv6 and IPv6 at the same time but they are fundamentally separate networks. As a result of this incompatibility IPv6 has caught on like a house on fire. That is, if the the house was made completely out of asbestos and at the bottom of a lake. IPv6 has been around since 1998 and was used by less than 1% of people up until 2013. More people have played <a href="https://play.google.com/store/apps/details?id=com.andrewtrumper.spacesmilies">Space Smilies</a>.<br />
<br />
Adoption is starting to pick up speed now according to <a href="https://www.google.com/intl/en/ipv6/statistics.html#tab=ipv6-adoption">google's IPv6 adoption statistics</a>. IPv6 adoption is somewhere between 6% and 7%. It's growing so rapidly I've had to update these numbers since I've been writing this blog post! For comparison, the number of people who use Macintosh computers to access the internet is also somewhere in this range. 6% is also much higher than the number of people who use Linux to access the internet which tends to be in the 2%-3% range.<br />
<br />
In Belgium the adoption rate is >30%. In the US it's about 15%. In Canada it's <mumbles>.. Yeah, let's just ignore Canada for now.</mumbles><br />
<br />
The rate of growth is accelerating. IPv6's adoption rate was around 2% at the start of 2014 and ~5% at the end. We're not even half way through 2015 and the IPv6 adoption rate is tickling 7%. Hurray!<br />
<br />
But so what?<br />
<br />
Well, I don't make enemies lightly, but one of my staunchest foes has been a technology called NAT. I first met this beast of a protocol while working on <a href="http://www.mysternetworks.com/index.php">Myste</a>r. Myster is a p2p program I wrote from about 1999 to 2008.<br />
<br />
NAT was created because even back in the old days of the 1990s we were running out of IP addressess. People has multiple computers that they wanted to use on one internet connection. Most of the time these tended to be larger organizations that had entire office buildings full of machines that were all networked together and wanted to be on the internet but there was no way to get enough IP addressed for them all.<br />
<br />
NAT allows you to to share one internet connection (and one IP address) for a very large number of computers. NAT is all over the place. Odds are very good that you are using NAT right now in your router. Heck, I am using NAT right now and NAT is my mortal enemy. That is to say I hope it's mortal. There's one large problem with NAT: it breaks the internet.<br />
<br />
Sure you and I both use the internet with NAT and everything seems ok, but that's a bit of a lie. The portion of the internet you use with a web browser works fine. Things like voice-over-ip and video games have issues. Not to mention the hundreds of programs that never were. You can't miss what you never had.<br />
<br />
Each IP address is like a phone number. If you're sharing a single phone number between people then when the phone ring it's not obvious who the call is for. For computers it means that incoming connections tend to get dropped unless you go through the pain of saying which type of connection should be answered by which computer. The same isn't true for outgoing connections which is why surfing the web works fine. (kowabunga dude!)<br />
<br />
In practice NAT is so common that most programs work around it. Skype, for example, is one HUGE work around for these kinds of problems. Skype is so crafty in getting around network restrictions that it sometimes acts more like a piece of malware. However, we are still losing out on cool technologies that can't exist because of NAT. Quite a few of these fall under the umbrella of p2p applications.<br />
<br />
Peer to peer has gotten a bad reputation for its association with copyright infringement. This is not a completely deserved association. While peer to peer programs like Napster were used to pirate music, peer to peer itself is <b>the </b>fundamental technology that makes the internet so awesome.<br />
<br />
Theoretically, every computer on the internet is a peer. Every computer has its own address and can communicate directly to each other without having to go through some intermediary. Given that this is the nature of the internet then why do we have this problem:<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://imgs.xkcd.com/comics/file_transfer.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://imgs.xkcd.com/comics/file_transfer.png" /></a></div>
<br />
NAT is the reason.<br />
<br />
IPv6 theoretically solves the NAT problem by making it possible for everyone and their toaster to have their very own IP address. This in turn means that individual devices can directly talk to each other on the internet. This in turn means that for the first time since about 1995 we will have a peer to peer internet. Think of all the new programs we could make!<br />
<br />
If anyone is thinking of building a p2p startup company, this is the time to do it. It will still take about 5 years before the internet is close to ready for a new p2p application so best start now. Did I ever tell our about my p2p application Myster? Next time I'll tell you how it works and how I'd like to enhance it to give it new features.Andrewhttp://www.blogger.com/profile/17391659235860245424noreply@blogger.com0tag:blogger.com,1999:blog-7451086006168569993.post-57973986081729833192015-05-11T20:36:00.000-04:002015-05-11T20:36:10.554-04:00Writing Scalable Code Using Inheritance in Java part 2In my last post I mentioned a few simple rules on how to write maintainable code using inheritance:<br />
<ol>
<li>Reduce method and member scope</li>
<li>Use final for methods and members</li>
<li>Make make overridable methods abstract</li>
<li>Don't make deep inheritance hierarchies</li>
</ol>
<div>
Today I want to show you how this affects maintainability.</div>
<div>
<br /></div>
<div>
Let's imagine we see a method like this in a class we know has many subclasses:</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">public final int getFoo() {...}</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
Here the presence of the final tells us that that we don't need to worry about anyone over-riding the behavior. Which is nice. </div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">public abstract int getFoo();</span></div>
<div>
<br /></div>
<div>
This one tells us sub classes *must* override this method. So we know that every sub class defines what we must do here. If we're in luck we'll be able to refactor this into a composition using the <a href="http://en.wikipedia.org/wiki/Strategy_pattern">strategy pattern</a>.</div>
<div>
<br /></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;">public int getFoo() {...}</span></div>
<div>
<br /></div>
<div>
This is problematic because we don't know whether there is subclass overriding this so we need to check all subclasses before we can modify its implementation. </div>
<div>
<br /></div>
<div>
If we want to over-ride this method in a subclass we need to check the superclass to make sure that it doesn't have any side effects or being called at an unexpected time. Since it's a getXXX() method, it shouldn't be doing any side effects.</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">protected final int calculateFoo(...) {...}</span></div>
<div>
<br /></div>
<div>
This method can be accessed by itself and sub classes but not-over-ridden by sub classes. This method is probably part of an API offered to sub classes for them to call.</div>
<div>
<br /></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;">protected abstract int calculateFoo(...);</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span></div>
<div>
This method can be accessed by itself and sub classes and *must* be overridden by sub-classes. If we're in luck we'll be able to refactor this into a composition using the <a href="http://en.wikipedia.org/wiki/Strategy_pattern">strategy pattern</a>.</div>
<div>
<br /></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;">protected int calculateFoo(...) {...}</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: inherit;">This is ambiguous. It might be a method that should be used as an API or it might define </span>behavior<span style="font-family: inherit;"> like in the Strategy pattern. A third alternative is that it is </span>meant<span style="font-family: inherit;"> to be over-ridden by sub-classes but provides a default implementation. Given the ambiguity I recommend adding a comment like this if it's the third case:</span></div>
<div>
<br /></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;">protected int calculateFoo(...) {</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> /* Can be overriden by sub classes</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> * default implementation: calculates foo using the bar algorythm </span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> */</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> ...</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;">}</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: inherit;">Although, again, using the strategy pattern with a default strategy is often the best way to go.</span></div>
<div>
<span style="font-family: inherit;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">private something() {..}</span></div>
<div>
<br /></div>
<div>
It can only be called in this subclass and can't be overriden so we're good.</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">public int foo;</span></div>
<div>
<br /></div>
<div>
Don't do that unless it's final, the class is final and your object doesn't have any methods in it. Something like this:</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">public final class Foo {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> public final int foo;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> public Foo(int foo) { this.foo = foo }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">}</span></div>
<div>
<span style="font-family: inherit;"><br /></span></div>
<div>
<span style="font-family: inherit;">This is a standard best practice.</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;">protected final int foo;</span></div>
<div>
<span style="font-family: inherit;"><br /></span></div>
<div>
<span style="font-family: inherit;">Don't do this.</span></div>
<div>
<span style="font-family: inherit;"><br /></span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;">protected int foo;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: inherit;">Don't do this either.. but more so.</span></div>
<div>
<span style="font-family: inherit;"><br /></span></div>
<div>
Next week: how to brush your teeth!</div>
Andrewhttp://www.blogger.com/profile/17391659235860245424noreply@blogger.com2tag:blogger.com,1999:blog-7451086006168569993.post-76713786674777852042015-05-10T12:00:00.001-04:002015-05-10T12:00:32.779-04:00Writing Scalable Code Using Inheritance in JavaEveryone should be familiar with the old advice that you should favor composition over inheritance. However, what if you find yourself in a situation where inheritance is the best tool for the job? There are a few things you can do to keep your code maintainable.<br />
<br />
Before we get into that I should explain a little about why you should favor composition over inheritance. Imagine you're trying to understand a class called Smilly. This class is detailing the behavior of a computer sprite in the award winning (in my mind) game Space Smilies. You're trying to figure out how this is implemented. The class is many lines long so it's obviously doing something something fancy. You notice that it inherits from a class called EvilThing. EvilThing extends from ComplexComputerSprite. This class extends ComputerSprite which extends AbstractSprite. All member variables are accessible from all subclasses. Where to start?<br />
<br />
Making a member variable protected means it's accessible to all sub-classes. Any one of these sub classes can read an write to them. You need to understand how the class and all the parent classes interact to create the desired behavior. Just as making a member as public is bad practice because to understand who is modifying that public variable you need to track down every piece of code that uses it, using protected members in a complex hierarchy is bad for the same reasons.<br />
<br />
This also applies to protected, non-final protected and public methods but it worse from the point of view of modifying the parent class. In order to understand if modifying a public or protected method will accomplish the desired behavior you need to understand the entire object hierarchy because there's no telling if one of the sub-classes has over-ridden any one of the public or protected methods and many make assumptions about how the class operate. If you stomp on one of its assumptions you'll cause a bug.<br />
<br />
Understanding the entire class hierarchy and how each class works with its parent is labor intensive to say the least.<br />
<br />
Because any method can be over-ridden anywhere in the hierarchy and because any state variable can be used by any subclass it means you have to be meticulous about checking every class in the hierarchy for unexpected overrides. With inheritance it's very easy to create a hierarchy that's so interconnected that you need to understand how everything works before you can understand any part of it. You might have an object composed of many classes but the classes over-ride each other's behavior in such a way that it effectively creates one big coupled class out of many.<br />
<br />
Composition, on the other hand, makes it easier to create and maintain encapsulation and lower coupling. It's easier to control which members and methods are accessed and there's no invisible over-riding of behavior to trip you up. Methods that are public make up the object's external interface. Simple as that. (Assuming the normal best practice of trying to hide your member variables and implementation inside by making them private).<br />
<br />
Composition is not as powerful as inheritance, however, so you might not be able easily do what you want using only composition. Which goes back to our original question, what do you do if inheritance is the right tool for the job? What if you need the power?<br />
<br />
Java has a few tools to help you out in the form of the keywords abstract, protected, private and final.<br />
<br />
If you find yourself with a sprawling inheritance hierarchy you can help out yourself and any future maintainer by:<br />
<br />
<br />
<ol>
<li>Reducing scope: If it can be made private then make it private. If it can't be made private then make it protected.</li>
<li>Adding final: If a method can be made final then make it final. It's one less method that can have sneaky override or one less variable that can be modified.</li>
<li>Make methods that *should* be overridden abstract: In complex objects it's more explicit to have all your overridable methods marked abstract so that's it's clear that subclasses need to implement these.</li>
<li>Don't make deep inheritance hierarchies. The rule I use is a class's true size includes all it's parent classes all the way up to java/lang.Object.</li>
</ol>
<br />
to be continued...Andrewhttp://www.blogger.com/profile/17391659235860245424noreply@blogger.com2tag:blogger.com,1999:blog-7451086006168569993.post-75374424630375942912015-03-27T14:38:00.000-04:002015-03-27T14:38:35.499-04:00Code KeyboardThe <a href="https://codekeyboards.com/">Code Keyboard</a> is blogger <a href="http://blog.codinghorror.com/">Jeff Atwood</a>'s idea of the ultimate keyboard for geeks. Since I'm a huge fan of <a href="http://www.andrewtrumper.com/2011/02/my-pet-computer-mice.html">buying a good mouse and keyboard</a> I bought one and I've been using it for 8 month now.<br />
<br />
The Code Keyboard is a high end mechanical keyboard. Mechanical keyboard use a spring instead of a rubber mat to make keys springy. Using rubber is cheaper but the feel isn't as good. The downside to mechanical keyboards is that they are expensive. Mechanical keyboards can be hard to find in stores and I hadn't used one in years so I was quite excited to get my hands on the Code Keyboard.<br />
<br />
I have the Cherry MX Clear variant of the keyboard and it's quite stiff. "Cherry MX Clear" refers to the type mechanical spring system used. There are a large number of <a href="http://deskthority.net/wiki/Cherry_MX">"Cherry MX" switch types</a> named after colors. Most mechanical keyboards use one variant or the other of these switches. For the "Clear" type you have to push surprisingly hard to get the key to go down. The keys are bouncier than rubber dome keyboards so it feels like your fingers are bouncing around on a trampoline. The effect is actually quite pleasant. I suspect I would have preferred the lighter action of the Cherry MX Brown version of this keyboard, though.<br />
<br />
Jeff's keyboard comes with a bunch of features designed to appeal to geeky keyboard enthusiasts. For example, on the back of the keyboard there are a row of switches that allow you to do things like swap the position of the caps lock and control keys. Or the alt and command keys if you're on a Mac. You can even set the keyboard to Dvorak or Colemak keyboard layouts.<br />
<br />
The keyboard even has a few features for gamers like the ability to disable to Windows key so you don't accidentally kick yourself out of the game. It also allows you to push 6 keys at the same time - the limit allowed by the USB protocol. The best feature, though, is the backlight. The keyboard glows in the dark for those late night gaming or coding sessions.<br />
<br />
The code keyboard is also visually minimalist. It doesn't have those silly extra media keys that don't work and just take up extra space on the keyboard making it look like fat Elvis. That doesn't mean there's no media key functionality but you have to sacrifice the menu key to turn it into a "fn" key to access that functionality. From the website:<br />
<br />
<blockquote class="tr_bq">
<div style="color: #333333; font-family: Arial, Verdana, Geneva, sans-serif; font-size: 18px;">
Lots of keyboards have <strong>multimedia keys</strong>, but almost none of them do it right. Either they require weird hand contortions to use, or they tack on a bunch of extra unnecessary buttons and knobs all over the keyboard in strange places.<br /></div>
<div style="color: #333333; font-family: Arial, Verdana, Geneva, sans-serif; font-size: 18px;">
Our solution is more elegant. On the CODE keyboard, the Fn key replaces the Menu key (provided you’ve enabled it via the switches on the back of the keyboard), and moves the media shortcuts to the navigation cluster. This configuration allows you to comfortably and logically access multimedia shortcuts with one hand – pressing Page Up to turn up the volume<em>just makes sense</em>. If you forget which keys do what, we’ve helpfully printed subtle glyphs on the front of each key, facing you, so you can see which keys have secondary functions.</div>
</blockquote>
<br />
The Code Keyboard is also well built. I mean old school soviet style well built. It's surprisingly heavy and feels very solid. You can use this thing for self defense purposes.<br />
<br />
I like the keyboard. The selling points for me are the mechanical switches and backlighting. I also like the minimalist design aesthetic and the fact they used a the standard keyboard layout with standard key sizes. None of this Microsoft style "Let's move things around for no good reason". Also the function keys are full sized, which is nice. I think I would have preferred the lighter action of the Cherry MX Browns, though.<br />
<br />Andrewhttp://www.blogger.com/profile/17391659235860245424noreply@blogger.com0tag:blogger.com,1999:blog-7451086006168569993.post-6144626007078368112014-12-18T00:03:00.001-05:002015-05-12T00:25:13.178-04:00Google wants to warn you every time you use HTTP instead of HTTPSSo recently, Chrome developers have been <a href="https://www.chromium.org/Home/chromium-security/marking-http-as-non-secure">floating the idea</a> that the UI should post a security alert every time the browser visits a page that isn't encrypted. <a href="http://www.bbc.com/news/technology-30505970">According to the BBC</a>, currently only 33% of websites use HTTPS (encryption). I suspect in actual practice the number of websites that are still using unencrypted web connections is much higher. This would mean you'd be getting many security alerts in practice.<br />
<br />
I am all for more encryption. There are far too many parties out there who have something to gain by snooping your connections. Every time I use an strange WI-FI hotspot I worry about who is listening or how they might modify my data.<br />
<br />
Many think this is all theoretical. That no one really cares about your data so unless it's something like banking data then it doesn't matter. Nonsense. Dangerously so. You're not up against humans you're up against software and with software you're never too small to not matter.<br />
<br />
Consider that the WI-FI hotspot might be inserting ads into web pages you're looking at. <a href="https://www.techdirt.com/articles/20140908/07191228453/comcast-using-packet-injection-to-push-its-own-ads-via-wifi-apparently-oblivious-to-security-concerns.shtml">Comcast has been caught doing this</a>. This is annoying and potentially misleading because now you can spam ads and the user will think it's coming from whatever website you're using. Hopefully they didn't make a mistake or the page won't show up at all. What about <a href="https://stackoverflow.com/questions/25438910/how-bad-is-it-to-replace-adsense-code-id-to-isps-adsense-id-on-free-internet">replacing existing ads</a> with your own? Too bad for the original web site trying to make a living. What about inserting a <a href="http://www.wired.com/2014/10/verizons-perma-cookie/">tracking ID</a> so you can be followed everywhere you go?<br />
<br />
What is the ISP <a href="http://broadband.mpi-sws.org/transparency/bttest.php">doesn't think you should be</a> watching youtube?<br />
<br />
And these are the corporations. Nasty people on the internet can snoop on everything that goes over an unencrypted connection. Much of it can be used to fool support and <a href="http://hackticool.com/post/75171875746">steal domain names</a> or <a href="https://medium.com/@N/how-i-lost-my-50-000-twitter-username-24eb09e026dd">accounts because why not</a>? .. to say nothing of <a href="https://en.wikipedia.org/wiki/Identity_theft">identity theft</a>. How much of yourself are you giving away each time you log into facebook?<br />
<br />
Then there's the government. Whether you're liberal or conservative you can bet there's someone who disagrees with something you're doing.<br />
<br />
Many websites have encrypted versions of their site. However it can be painful to figure out which sites have an encrypted version and to manually switch over. This is where <a href="https://www.eff.org/Https-everywhere">HTTPS Everywhere</a> comes in.<br />
<br />
<blockquote class="tr_bq">
HTTPS Everywhere is a Firefox, Chrome, and Opera extension that encrypts
your communications with many major websites, making your browsing more
secure. <b>Encrypt the web: Install HTTPS Everywhere today.</b> </blockquote>
<br />
HTTPS Everywhere is a browser extension that contains a <a href="https://www.eff.org/https-everywhere/atlas/">database of web sites</a> that have encrypted versions and automatically redirects you to the encrypted version of the site without you having to worry about it. This gives me some piece of mind when I'm using public WI-FI hotspots. It's not perfect but it's the best we can do until all connections on the internet are encrypted.<br />
<br />
.. and they will be.Andrewhttp://www.blogger.com/profile/17391659235860245424noreply@blogger.com0tag:blogger.com,1999:blog-7451086006168569993.post-74676856801365324712014-12-10T18:00:00.001-05:002014-12-10T18:00:33.527-05:00I Found a Good HeadsetThose with long memories will remember that <a href="http://andrewtrumper.blogspot.ca/2014/11/headsets-and-things.html">I have been looking for a good circumaural headset </a>ever since my <a href="http://andrewtrumper.blogspot.ca/2010/10/new-usb-microphone-plantronics-655.html">Plantronics 655</a> headset died.<br />
<br />
The Plantronics 655 was never the perfect headset. Its ear cushions were too small and rested on your ears so that they would become uncomfortable after wearing them for a long time. Just about every headset has this problem. I was pleasantly surprised to find that the KOSS <a href="http://www.koss.com/en/products/headphones/full_size_headphones/SB45USB__SB45_USB_Communication_Headsets">SB45</a> doesn't. Its ear cups are large enough to go all the way around the ears so I bought a pair. I am very pleased with them.<br />
<br />
My only complaints are that they exert slightly more pressure on the sides of my head then I'd like and that they don't do whatever magic the Plantronics 655 headset does to let you hear yourself when you're on Skype.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-JneBsQalV6c/VIjQiElJX8I/AAAAAAAAAgo/9DM5QpS5Ok0/s1600/SB45%2BUSB.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-JneBsQalV6c/VIjQiElJX8I/AAAAAAAAAgo/9DM5QpS5Ok0/s1600/SB45%2BUSB.jpg" height="320" width="320" /></a></div>
<br />
<br />
Let me explain, you know how when you wear a headset you can't hear
your own voice very well? The 655s play your own voice back to you so you can hear yourself. Since you can hear yourself you don't feel the need to shout. I'm actually surprised since I thought it was a feature of Skype but it works with the 655s and not the SB45s. It looks like some sort of device level feature. It's really useful and I miss it.<br />
<br />
Apart from that the KOSS SB45 headset is very comfortable, has a good mic, good sound, inexpensive and I would recommend it.Andrewhttp://www.blogger.com/profile/17391659235860245424noreply@blogger.com0tag:blogger.com,1999:blog-7451086006168569993.post-33004758539132746702014-12-08T20:39:00.000-05:002014-12-08T20:39:15.391-05:00Space Smilies now on Google Play storeWell, I've <a href="https://play.google.com/store/apps/details?id=com.andrewtrumper.spacesmilies&hl=en">released Space Smilies to the Google Play Store now</a>. Go download it! Have fun! <a href="mailto:spacesmilies@gmail.com">Give feedback!</a><br />
<br />
My plan for the sabbatical was to release two video games. The first was this one. I figured it would take about a week to get it ready for release. If I hadn't decided to change things that would have been a realistic estimate. Instead I decided to clean up the Space Smilies movement, add levels, add a level editor, redo the graphics and things like that. I figured with all that it would take a month. It took about 4 months. This plus a bunch of other demands on my time mean that I'll probably not get to do the game and game editor I wanted to.<br />
<br />
Ah whatever.<br />
<br />
I have other projects to work on. In fact, it's quite hard to set priorities. Part of the problem with deciding on what project to undertake is that it's not clean what's worthwhile unless your part of the conversation. That and <a href="http://www.mysternetworks.com/">Myster </a>set the bar for success really high. We would get 10000 downloads a day when we released a new version. Most days we'd only get 300 downloads. That's still impressive. It would be even more impressive if I hadn't made a bunch of newbie errors early on in my installers that made most users simply not able to use the application on Windows. Painful doh!<br />
<br />
Every field has a conversation. If you're a Starcraft player you can think of it as the current state of the meta game. It consists of what is known, what is done, what needs to be done and what is not worth doing. Actually, it's more complex than that includes all the little arguments that are in progress and all the relationships, camps and tribes that are squabbling at the moment. I used to be very connected to these things but some of the conversations have moved in 10 years.<br />
<br />
Games and indy gaming especially. The tools available to modern indy game developers are impressive. Part of me is saddened by the fact that application development frameworks are no where near as good.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-26I9Vx6N_IY/VIZSmFJqaXI/AAAAAAAAAgQ/Q7AVWxf3CFs/s1600/y%2Bu%2Bno%2Bawesome.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-26I9Vx6N_IY/VIZSmFJqaXI/AAAAAAAAAgQ/Q7AVWxf3CFs/s1600/y%2Bu%2Bno%2Bawesome.jpg" height="240" width="320" /></a></div>
<div style="text-align: center;">
<br /></div>
<br />
This means that creating a game is much more about learning the tools than learning exotic programing techniques. I'm not sure I want to bother to learn a tool whose sole purpose it to quickly make top scrolling video games. I would love to WRITE such a tool. In fact, that was kind of the idea, but it looks like I am 5 years too late there.<br />
<br />
Doh. That's what happened when you don't pay attention.<br />
<br />
Oh well, I'll figure out something. Stay tuned. :-)Andrewhttp://www.blogger.com/profile/17391659235860245424noreply@blogger.com0tag:blogger.com,1999:blog-7451086006168569993.post-63743298163948329962014-11-26T17:14:00.002-05:002014-11-26T17:14:32.157-05:00Android API First ImpressionsI've recently finished my game and have started porting some of the Android only sections back to JavaSE. I have been struck by how everything seems so much easier with the Android API.<br />
<br />
I find this very surprisingly. I've been using Swing for a very long time and have gotten good at doing crazy things with it. Even with my knowledge of Swing tricks and hacks it's still easier to do things on Android than Swing.<br />
<br />
Take, for example, layouts. I've gotten to the point with Swing where I just use GridBagLayout from the start. I've got my own utilities that makes using <a href="http://madbean.com/anim/totallygridbag/">GridBagLayout much less painful</a> than it ordinarily would be. Gridbag is surprisingly flexible, if you've managed to survive its brutal learning curve. That said, doing layout with Android's isn't too bad. I certainly didn't have the parade of WTF moments that I experienced trying to wrap my head around Swing's layout system. Android also has the advantage of coming with a GUI layout editor. It means you don't find yourself blindly changing values and recompiling/relaunching every time to see if the changes did anything.<br />
<br />
Then there are things like how do you make all the widgets translucent with an animated background? With Swing, I had to use the arcane knowledge I've discovered in my 10 years or so working with it. With Android it's just a property on the view or layout. Golly, that's convenient.<br />
<br />
What about have multiple layouts on the screen at the same time and showing/hiding them like cards? With Android it's that way by default. With Swing it's a weird system of content panel layers or you can use CardLayout. Either way it's which is full of the usual Swing WTF moments: <a href="https://docs.oracle.com/javase/tutorial/uiswing/layout/card.html">I have to do that to show a "card"</a>? <a href="https://docs.oracle.com/javase/tutorial/uiswing/components/rootpane.html">JLayeredPane is where</a>?<br />
<br />
Events are handled nicely too but to be honest, I was hoping for more here. Swing's event system is actually not too bad. Android's is very similar but also offers additional flexibility: you can define your event handler method directly in the layout. This is cute but this won't scale well if you have a fairly complex Application. That pretty much sums up my feelings towards Android event handling: It's good and tries to make things really convenience but the convenience comes at the price of preferring and unscalable application architecture.<br />
<br />
Well, I say that but Android's encourages an application architecture that segments large applications into multiple activities which would help with scalability a great deal. I don't know how these two conflicting factors work out in real applications but I'm certain that either way there's more than enough flexibility in the approaches you can take to make everything work.<br />
<br />
So, in conclusion, my experience with Android's APIs has been quite positive so far. I'm looking forward to more.<br />
<br />Andrewhttp://www.blogger.com/profile/17391659235860245424noreply@blogger.com0tag:blogger.com,1999:blog-7451086006168569993.post-80246257995108825232014-11-18T15:51:00.001-05:002014-11-18T17:20:09.500-05:00AgileWhenever I hear someone start to talk about <a href="https://en.wikipedia.org/wiki/Agile_software_development">Agile methodologies</a> I start to worry because, while the industry has agreed that Agile is the way to go, Agile is often misunderstood.<br />
<br />
Agile is often sold to management as a way of getting better quality
software faster, with fewer bugs showing up in the field. Well, that might happen as a side effect but Agile is really about being flexible. Change is a big problem on any engineering project. With most engineering disciplines change is very often fatal but at least it's easy to understand why. If you design an engine for a car and it doesn't fit, you're basically screwed. This is where the saying "measure twice cut once" comes from. With software it's not as obvious sometimes why a change would be particularly difficult because software is just a bunch of instructions. Just change the instructions! Duh.<br />
<br />
It's never that simple. Software isn't constrained by the laws of physics. As a result, software projects have a tendency to grow in complexity until they become unmanageable. A typical project is a tangle of inter dependencies. Some of these dependencies are design assumptions, some of them are organizational assumptions - like budgeting and estimates. For example, if you write a piece of software for a desktop computer and find out that it really needs to run on a smartphone, you're basically screwed. It's doesn't matter that it's software.<br />
<br />
Agile methodologies are a series of mitigation you build into your software <b>and </b>organization to make it resilient to changes during development. Those changes can be discovered difficulties or they could be mistakes.<br />
<br />
<br />
In order to get into the Agile mindset you must first be convinced that planning is pointless. That the world is too full of unknowns and surprises that trying to plan is like putting on a contact lens in the middle of a sandstorm.<br />
<br />
Basically,<br />
<br />
<ol>
<li>Your time estimates are random numbers</li>
<li>The man in charge of the requirements is a raving madman</li>
<li>The chief architect has some kind of dementia</li>
<li>What you're trying to build might be a logical impossibility anyway</li>
</ol>
So what does all this mean? It means you can't rely on estimates, the
requirements still need to be discovered, you're going to make mistakes
at the design phase and the whole thing might be a waste of time anyway.<br />
<br />
In other words, it's a typical software project.<br />
<br />
The only one thing you're not allowed to assume is that your programmers are idiots or are evil. If your programmers aren't excellent, trustworthy professionals then you're doomed no matter what you do. You might as well go outside and play Frisbee all day. You'll fail either ways but Frisbee is more fun.<br />
<br />
<br />
Agile mitigation consist of things like this:<br />
<ul>
<li>Chopping the project into many small pieces</li>
<li>Prioritizing these pieces with the goal of getting something useful quickly</li>
<li>Doing each piece one at a time; avoid over-design - <a href="https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it">YAGNI</a></li>
<li>Giving this "something useful" to the customer and finding out if you're on the right track as soon as possible</li>
<li>Re-evaluating the priorities of these pieces every day as new information is discovered during development</li>
<li>Pushing decisions to the edges of the org chart (developers or other) to allow developers to solve problems without a heavy vetting process (self organizing teams)</li>
<li>Lightweight, flexible and adaptable process - one that allows people to adapt to changes</li>
<li>Improve communication channels between people (co-location, burn down charts to track progress, bug database, unrestricted channels (anyone can talk to anyone else in the organization))</li>
<li>Techniques to <a href="https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it">write maintainable code</a>. Unmaintainable code is by definition hard to change.</li>
</ul>
<br />
My key point is that Agile methodologies don't make change free. It doesn't make software development magically faster or higher quality. It's all about being flexible and mitigating the damage done by routine changes during software development.Andrewhttp://www.blogger.com/profile/17391659235860245424noreply@blogger.com0tag:blogger.com,1999:blog-7451086006168569993.post-12492052252565289812014-11-17T16:49:00.003-05:002014-11-17T16:51:54.271-05:00Headsets and thingsA little while ago I wrote a blog post about replacing my <a href="http://andrewtrumper.blogspot.ca/2010/10/new-usb-microphone-plantronics-655.html">Plantronics 655</a> headset that had recently stopped working. Well, I spent a great deal of time searching for a good replacement and eventually came to the conclusion that there aren't any good headsets out there. Every single one of them has issues. Either the headset ear cups are not large enough, or the mike doesn't work very well or worse.<br />
<a href="http://www.razerzone.com/ca-en/gaming-audio/razer-kraken-usb"><br /></a>
<a href="http://www.razerzone.com/ca-en/gaming-audio/razer-kraken-usb">Razer Kraken USB</a> isn't too bad. I didn't like the circular earpieces. They were too small (5cm) and the wrong shape. Ears aren't circular, not sure why they were with circular ear cups. Most of the reviews complained they fit funny.<br />
<br />
The <a href="http://www.google.ca/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&cad=rja&uact=8&ved=0CB0QFjAA&url=http%3A%2F%2Fsteelseries.com%2F&ei=l2VqVNupN4GvyATm5ICYBA&usg=AFQjCNEcN2IVh9blimvA6JC_UYC7ilJtkg&sig2=t8u_Mn0veUEHnow_On4VEg&bvm=bv.79908130,d.aWw">Steel Series</a> headsets had terrible microphones. I like <a href="http://www.google.ca/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&cad=rja&uact=8&ved=0CB0QFjAA&url=http%3A%2F%2Fsteelseries.com%2F&ei=l2VqVNupN4GvyATm5ICYBA&usg=AFQjCNEcN2IVh9blimvA6JC_UYC7ilJtkg&sig2=t8u_Mn0veUEHnow_On4VEg&bvm=bv.79908130,d.aWw">Steel Series</a> as a company. My mouse is a steel series XAI but I would be embarrassed to use such a terrible microphone.<br />
<br />
The worst, however, was the <a href="http://en-ca.sennheiser.com/">Sennheiser</a> headset. Sennheiser has a fantastic reputation online. So good in fact, that when I found a pair of PC333D G4ME going for cheap I bought them right then and there. They are normally out of the price range I would spend on a headset even with the sharp discount I got them for so I was hoping they would be amazing. Nope. Crushingly disappointed. Literally. They actually crushed my head with such force I couldn't wear them.<br />
<br />
Have you even been back to an elementary as a grown man and tried to sit down at one of those tiny desks. That's pretty much what it felt like trying to put on the PC333D G4ME. I should point out I have never had anything close to this experience before. All headsets I have ever tried fit nicely on my head with plenty of room to spare. It's starting to make me wonder: do you have to grow up with this headset? Is it like <a href="https://en.wikipedia.org/wiki/Artificial_cranial_deformation">artificial cranial deformation</a>? You start off as a toddler playing games with this headset and over time your skull changes to fit the headset?<br />
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://upload.wikimedia.org/wikipedia/commons/thumb/5/57/D%C3%A9formation_P%C3%A9ruvienne_MHNT_Noir.jpg/800px-D%C3%A9formation_P%C3%A9ruvienne_MHNT_Noir.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="304" src="https://upload.wikimedia.org/wikipedia/commons/thumb/5/57/D%C3%A9formation_P%C3%A9ruvienne_MHNT_Noir.jpg/800px-D%C3%A9formation_P%C3%A9ruvienne_MHNT_Noir.jpg" width="320" /></a></div>
<div style="text-align: center;">
<span style="font-size: xx-small;">Picture of typical Sennheiser customer skull</span></div>
<br />
<br />
I had to send the PC333D G4ME back to the online retailer with a financial penalty so that has left me grumpy.<br />
<br />
What surprised me is that it's very hard to find a headset with ear cups that are properly circumaural. All of them seem to have ear cups that are about a centimeter too small. Except for Plantronics whose ear cups are 2 cm to small. If you're wearing a headset all day they will press on your ears and become uncomfortable.<br />
<br />
I was also surprised that many manufactures (some not explicitly mentioned here) ship their headsets with terrible microphones. If you're gaming online this tends to only annoy other people. However, if you're trying to do digital dictation, a good microphone is important.<br />
<br />
The one manufacturer I haven't tried is Koss. This is partly because I can't figure out where Koss products are sold in Montreal and don't want to play the online ordering lotto again. The most comfortable headphones I own are Koss. Talking to their sales staff they might have a headset with large enough ear cups. I say might because no one at the company would commit to any measurements so I am still not sure.<br />
<br />
At the moment I'm using a new pair of <a href="http://andrewtrumper.blogspot.ca/2010/10/new-usb-microphone-plantronics-655.html">Plantronics 655</a>. They aren't the most comfortable headset ever but they were dirt cheap, they have a good microphone and they don't crush my skull. I'll continue to use them until I can get a decent replacement.Andrewhttp://www.blogger.com/profile/17391659235860245424noreply@blogger.com0tag:blogger.com,1999:blog-7451086006168569993.post-79553224700369679642014-11-12T14:30:00.001-05:002014-11-12T14:30:34.481-05:00Space Smilies (beta)For the last 4 months or so, I've been writing a video game and it's now at the beta testing phase.<br />
<br />
The game is called Space Smilies and is similar to space invaders but with a few differences that show themselves as the game progresses. It also includes a built in level editor so you can tweak your own levels simply by adjusting a few parameters.<br />
<br />
The game is based on the old Space Smilies code that made<a href="http://andrewtrumper.blogspot.ca/2010/04/space-smillies-technological.html"> an appearance on this very blog</a> back in 2010. That code was in turn based on a video game I wrote in 1999 while studying at University so the code has had a long history.<br />
<br />
The game is built for the Android platform, so if you have an Android phone (or Android compatible) you can try it out below:<br />
<br />
<a href="https://dl.dropboxusercontent.com/u/361138/Space%20Smilies%20Release.apk">Space Smilies for Android</a><br />
<br />
Note, the link above is updated with new versions as they are built so if you want to get the latest build all you need to do is re-download it using the same link.Andrewhttp://www.blogger.com/profile/17391659235860245424noreply@blogger.com0tag:blogger.com,1999:blog-7451086006168569993.post-25304421751587570692014-11-10T14:53:00.001-05:002014-11-10T15:31:25.218-05:00Nexus 5 and garbage collectorsI really like my Nexus 5. It's at the level of refinement where I would say everything works more or less correctly. This makes a change from my previous smart phone the Samsung Galaxy S. That was a terrible phone. The Galaxy S was too slow, didn't work as a phone owing to a terrible microphone, didn't work as a GPS, would flatten a battery in about 4 hours if you forgot to turn off the GPS or wi-fi and would crash fairly often too. I initially got the Galaxy S due to its reputation as a sort of landmark phone. I figure that means that phones before it were even worse somehow.<br />
<br />
So, anyway, I like my Nexus 5. It works as a phone, GPS, keeps a charge and is stable. It also does a cute impression of a flashlight, has a decent camera and can make a sweet Mango Lassi. Well, everything except that last one. I feel like some kind of digital wizard carrying it around. Need a light? Boom! No problem. Need to know the weather? Boom! Weather radar! Need a map of Belgium? Well, that's random but I can get that for you too.<br />
<br />
It's also been a good testbed for my application. I've learned quite a few things using that phone like....<br />
<br />
Did you know that Android's garbage collector doesn't do compaction? For me this is a little like learning that Ferrari's new car is steam powered; it's a bit difficult to wrap my head around. I figure there some good, technical reason why they do this. I'd wager that they were trying to avoid garbage collector pauses. Google seems to obsessed with avoiding pauses or stuttering on Android (which they call "jank"). The thing is, if you don't have compaction you could run out of memory without.. umm.. running out of memory!<br />
<br />
Compaction is the step that un-fragments memory. Wikipedia has good article on <a href="https://en.wikipedia.org/wiki/Fragmentation_%28computing%29">memory fragmentation</a> but basically it's when memory gets filled with lots of little holes. Think of it like empty seats in a movie theater. If you arrive too late all the free seats are in singles or groups of two and are scattered all over the place. If you're in a group or four and want to sit together you can't because there aren't four seats together. Compaction is the step where you politely ask people to move around so all the free seats are in one big row - that way large chunks of memory can sit together. If large chunks of memory can't sit together you get an out of memory error even though there's technically enough free seats.<br />
<br />
I think I mixed my memory-aphores there.. but you know what I mean.<br />
<br />
Luckily the latest release of the Android operating system includes code that does compaction. Although they seemed to imply that it will only do it when you switch out of the application or something. I guess the garbage collector pauses are hard to notice when you've switched out of the application. Does this mean you'll need to switch out of the application you're using every once in a while to avoid running out of memory? I hope not. :-)Andrewhttp://www.blogger.com/profile/17391659235860245424noreply@blogger.com0tag:blogger.com,1999:blog-7451086006168569993.post-34011917018043694702014-11-07T16:56:00.003-05:002014-11-07T16:56:28.199-05:00The last 10%Well, that was a fun month or two. I've been working hard on my video game - getting it ready for release. It's so close to being completed I can smell it. To the extent that you can smell software, that it. The one major take away lesson I've learned so far is that software development is slow. Agonizingly slow. <br />
<br />
I actually knew this already. I've been writing software for a good 15 years both on my own and in large teams, but it's just hit me again: software development takes forever.<br />
<br />
Someone said, the first 90% of the product takes 90% of the time. The
second 10% of the product also takes 90% of the time. This is pretty
good summary.<br />
<br />
The thing that trips you up is it's relatively quick to get something basic working. So if all you've ever done is a little scripting for yourself or in-house tools you're getting a warped perception. Once you want to release software commercially to actual users the quality needs to be much better. And I'm writing software in the consumer space - where no one cares that it's hard and there are no second chances; it has to be perfect first time.<br />
<br />
For example, with this game I'm writing it took me about two weeks to get the program to work correctly when it's in the background. You would think this would be fairly simple. After all, it's unlikely you're ever run into an application on Android that's ever had issues when put into the background. Well, my game was crashing. After I fixed that crashing, the game would continue to play itself while in the background. After I fixed that the music would do the same. Then I had to release the graphics to free up memory to be a good Android citizen or risk being barred from Google Play store. Finally I had to correct an edge case where locking the phone would cause the application to reset.<br />
<br />
It takes time to understand how things work on a new platform and how to design an architecture that works with the platform instead of fighting it. Throughout the process you can't help but think you're not adding anything to the product; I'm not adding levels or new characters or anything to the game. I'm just fixing stuff that should just work anyway. It also doesn't demo well: Look! Doing this doesn't crash! It did before?<br />
<br />
I don't mind though. In fact, it's what attracts me to the consumer space; because consumers have a choice. People aren't being saddled with your software because their organization chose it for them. They aren't being forced to use it because that's what their client uses. When someone uses your software it's because they want to. No IT department to help with the migration. No pressure to conform. You have to convince people to use it. In this sense it's a more honest type of software development. I've always had high expectations from the software I use and I've observed that, in aggregate, when they have a choice, human beings do too.Andrewhttp://www.blogger.com/profile/17391659235860245424noreply@blogger.com0tag:blogger.com,1999:blog-7451086006168569993.post-67035964060578469222014-09-26T17:21:00.000-04:002014-09-26T17:21:57.391-04:00My Video Game's LeaderboardSo, for the last for days I've been working hard on my game and generally ignoring blog related activities. This has resulted in two things. The first is that there's been no blog posts but the second is that I can finally see the light at the end of the project. The game I'm writing is nearing completion and this make me happy.<br />
<br />
I decided to stop working on the blog when I started working on the global leaderboard for my game. The idea is that instead of having a top ten list only for the local machine you have one giant one for the entire world.. Consequently it has to have more than ten entries in it. I figure the world must contain several dozen people so I should use a database for that sucker (I'm not actually sure of the world's population since I don't go outside anymore :( ).<br />
<br />
The problem with databases is that I've spent most of my time as a professional developers trying to avoid SQL. I don't like SQL, It reeks too much of command lines and the 1970s. My internal conceptually associations go something like silly hair, the colours orange and brown, tiny tennis rackets and SQL. SQL is an injection attack just waiting to happen. Seriously. You need to escape stuff properly or use an API with prepared statements. Otherwise the teenage equivalent of me is going to turns the database into a playground.<br />
<br />
In order to hook up the leaderboard to my application I also needed to use Apache and PHP. I chose these two things because my ISP chose these things and was nice enough to let me use them.<br />
<br />
In order to write my code without affecting production I needed to create a test environment. This proved to be extremely difficult partly because I'm on Windows and this is considered weird by the UNIX crowd who make all this stuff (also 1970s, BTW). But mostly because I was trying to match all the version numbers with the ones my ISP had installed. It's generally a good idea to test on the same software as you deploy on because it has a better chance of working. In this case that would probably require time travel as the ISP hasn't updated its software since dinosaurs roamed the earth.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-VUipWDFE0dg/VCXYDgTwAkI/AAAAAAAAAfM/VXC5r5Hprls/s1600/leaderboard.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-VUipWDFE0dg/VCXYDgTwAkI/AAAAAAAAAfM/VXC5r5Hprls/s1600/leaderboard.png" height="320" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<span style="font-size: xx-small;">Space: The go-to place for all good video games</span></div>
<br />
In any case, I eventually got a decent test setup using fairly up-to-date versions of everything but it took just about a week to do. Normally I wouldn't mind too much since I'm being paid to do it, however in this case I was fully aware I was wasting my own time and could probably build <a href="http://www.mysternetworks.com/index.php">my own server software</a> quicker at this point (it never works out this way but it feels like it should).<br />
<br />
Anyway, the general upshot is that my game now has a global (as well as local) leaderboard. Hopefully gamers will like that and compete for higher rankings. If they don't I just wasted a huge chunk of time.<br />
<br />Andrewhttp://www.blogger.com/profile/17391659235860245424noreply@blogger.com0tag:blogger.com,1999:blog-7451086006168569993.post-72159305496543265542014-09-09T15:21:00.002-04:002014-09-09T15:24:44.401-04:00Advanced Settings BlogAt some point someone noticed that there was a bunch of stuff in the setting panel. "This is too cluttered!", someone said, "Some of this stuff must be for power users! Put those settings in an 'advanced' section.".<br />
<br />
Ah, but what is "advanced"? This is, apparently, a hard question to answer because every thing I want to change seems to be in the "advanced" section.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-nblqIxUmCMU/VA9OK13xmNI/AAAAAAAAAe8/UO6mVq53WK8/s1600/every_setting_panel_ever.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-nblqIxUmCMU/VA9OK13xmNI/AAAAAAAAAe8/UO6mVq53WK8/s1600/every_setting_panel_ever.png" height="317" width="320" /></a></div>
I would suggest a few guidelines:<br />
<ol>
<li>"Advanced" is not a synonym for miscellaneous. Just because it's not used or doesn't fit into any other category doesn't mean it's "advanced".</li>
<li>"Advanced" is not a synonym for rarely used. Sure, I rarely delete a password using the password manager but deletion of a remembered password isn't rocket science.</li>
<li>"Advanced" does not mean hidden. Sometimes you need to get technical, that doesn't mean it has to be hidden like some kind of video game <a href="http://diablo.wikia.com/wiki/The_Secret_Cow_Level">secret level</a>. It's getting to the point I need to google the entire internet to get the Konami code to change a setting.</li>
<li>If >90% of users don't know what it means and it can seriously screw things up by messing with it, then it's an advanced setting. Be sure to include a "reset to defaults" button somewhere. Maybe a help button too since you are allowed to try and help interested users understand things. You're just not allowed to assume that they'll read it.</li>
</ol>
<div>
<br /></div>
Andrewhttp://www.blogger.com/profile/17391659235860245424noreply@blogger.com0tag:blogger.com,1999:blog-7451086006168569993.post-28083171211349514482014-09-04T17:47:00.002-04:002014-09-04T17:47:32.458-04:00Headset replacementSo my <a href="http://andrewtrumper.blogspot.ca/2010/10/new-usb-microphone-plantronics-655.html">Plantronics 655</a> headset recently stopped working. This is annoying but it does gives me a good excuse to get a better one. While I was impressed with the decent microphone and lack of any background hiss, the Plantronics headset had some issues. The biggest problem was that it became very uncomfortable over time.<br />
<br />
Note to self: <a href="http://en.wikipedia.org/wiki/Headphones#Supra-aural">supra-aural </a>headphones are not good for wearing long term.<br />
<br />
For my headset, I've been looking at gaming gear because I've come to learn that, <a href="http://andrewtrumper.blogspot.ca/2011/02/my-pet-computer-mice.html">for some things</a>, gamers are the most demanding. Best audio quality, best microphones, best comfort, rugged etc.. Gaming headsets need to do all these things.<br />
<br />
I'm also determined to get a USB headset because I'm addicted to the quiet. To quote myself:<br />
<blockquote class="tr_bq">
<span style="background-color: white; font-family: 'Trebuchet MS', Trebuchet, Verdana, sans-serif; font-size: 13px;">Usually a headset plugged into the stereo mini jack on the computer will create a tiny background hum or hiss all the time. This is typically because the audio card on the machine isn't perfectly isolated from all the electrical noise coming from inside the computer. Because this is a USB headset, however, there is none of that. It sounds as if the headphones are not plugged in, as if the computer is not playing any sound.</span></blockquote>
<br />
So, to re-cap, I'm looking for a circumaural set with good audio, good microphone (ideally one that doesn't pick up room noise), and is USB.<br />
<br />
My current lead contender is the <a href="http://www.razerzone.com/ca-en/gaming-audio/razer-kraken-usb">Razer Kraken USB</a>. Razor has many headsets available. Most of the seem to be named the Kraken. Razer has a <a href="http://www.razerzone.com/gaming-audio/kraken">web page</a> so you can wade through the morass of Kraken models. Want to know the difference between the <a href="http://www.razerzone.com/gaming-audio/razer-kraken-71-chroma">Razer Kraken 7.1</a> (which is USB) and the Razer Kraken USB (which also does 7.1 audio!)? Heavy sigh. The Razer Kraken USB is relatively inexpensive too so I won't feel to bad next time I sit on them and they break.<br />
<br />
There are many other contenders though and I'll be looking through the online reviews and checking them out. Until next time...Andrewhttp://www.blogger.com/profile/17391659235860245424noreply@blogger.com0tag:blogger.com,1999:blog-7451086006168569993.post-36418066864996044492014-08-27T14:52:00.002-04:002014-08-27T14:52:19.285-04:00Scala course on Coursera by Martin Odersky<a href="http://en.wikipedia.org/wiki/Martin_Odersky">Martin Odersky</a>, the creator of Scala, is doing an <a href="https://www.coursera.org/course/progfun">online course</a> on <a href="https://www.coursera.org/">Coursera</a> starting September 15th of this year (2014). It's an advanced course ment for those who already know a programming language like Java or C# although knowing languages like C/C++, Python, Javascript or Ruby will also work. Since I am actually in the process of reading <a href="http://www.artima.com/shop/programming_in_scala_2ed">Martin Ordersky excellent Scala book</a>, this works out nicely for me. The first edition of that book is available for <a href="http://www.artima.com/pins1ed/">online reading</a> too.<br />
<br />
Scala is an attempt to create a language for the <a href="http://en.wikipedia.org/wiki/Java_virtual_machine">JVM</a> that is completely compatible with existing Java code but pushes further than Java. Scala adds things like lambdas and function programming concepts while trying to address criticisms of Java like its verbosity. Scala is a language that has been picking up steam recently and it seems to be where the <a href="https://www.youtube.com/watch?v=RnqAXuLZlaE">cool JVM people</a> are hanging out (<a href="https://www.youtube.com/watch?v=b-Cr0EWwaTk">assuming it's possible to be cool with Java</a>).<br />
<br />
<br />
<br />
<br />
<br />
Ok, <a href="https://www.youtube.com/watch?v=Mk3qkQROb_k">one more ridiculous video</a>: is that a Mac Plus? Pff, Java doesn't run on that.Andrewhttp://www.blogger.com/profile/17391659235860245424noreply@blogger.com0tag:blogger.com,1999:blog-7451086006168569993.post-25947154163851404702014-08-26T17:56:00.001-04:002014-08-26T17:56:45.436-04:00final Keyword in JavaI like the "final" keyword in Java. However, I'd like it more if every reference were final by default and "muttable" was a the keyword to create a mutable a reference. This way around is better because if you see "mutable" in a reference declaration then you know that the author took the time to do that because they <b>are </b>mutating the reference. Mutation is the case you want to watch out for and discourage. Today, if a reference isn't marked as final you don't know if that means it's mutated or if the programmer isn't using final. In my experience mutability is the rarity and final is the common case.<br />
<br />
Unlike C++, Java doesn't have a way of making both the reference and object constant. In java the declaration.<br />
<br />
<span style="font-family: Courier New, Courier, monospace;">public class Foo {</span><br />
<span style="font-family: Courier New, Courier, monospace;"> private final Date someDate = new Date();</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">// ....</span><br />
<br />
Doesn't stop you from mutating the object like this:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;"> someDate.setTime(1234); </span><br />
<span style="font-family: inherit;"><br /></span>
<span style="font-family: inherit;">It just stops you from changing the reference like this:</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"> someDate = new Date(); // not allowed, referenced is final</span><br />
<br />
Some programmers use the final keyword to mean that not only shouldn't the reference change but the object shouldn't either. Restricting final in this way is not a good idea. Basically, that's not what final means and if you do that you're not helping anything.<br />
<br />
I am generally against using language keywords like final to mean something other than what the compiler can assert from them. Marking a reference final doesn't guarantee that the <b>object </b>won't change. You might as well add a comment to the declaration like this:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;">public class Foo {</span><br />
<span style="font-family: Courier New, Courier, monospace;"> // someDate is not mutated</span><br />
<span style="font-family: Courier New, Courier, monospace;"> private final Date someDate = new Date();</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">// ....</span><br />
<span style="font-family: inherit;"><br /></span>
<span style="font-family: inherit;">Because it has about the same chance </span><span style="font-family: inherit;">of either being honoured or going out of date.</span><br />
<span style="font-family: inherit;"><br /></span>
<span style="font-family: inherit;">Additionally, knowing that the reference is final (not the object) is still very </span>useful<span style="font-family: inherit;"> on its own. This is why C++ has the ability to make both the object and reference constant independently. By using final to mean the object and reference shouldn't change you confuse maintainers with an idiosyncratic style and destroy the utility of final-for-references-only.</span><br />
<span style="font-family: inherit;"><br /></span>
<span style="font-family: inherit;">If you want to create an object that doesn't change then create an <a href="http://en.wikipedia.org/wiki/Immutable_object">immutable object</a>. (see the link)</span><br />
<span style="font-family: inherit;"><br /></span>
I have to say that I personally don't make function parameters final because it's too much bother. I've found it looks like noise in the code and other developers resent it. Instead I have the compiler warn me if there's any assignment to a method parameter, which accomplishes the same thing.<br />
<br />
If you are using eclipse you can turn that on by going to Window -> Preferences -> Java -> Compiler -> Errors/Warnings and turning on the "Code Style -> Parameter Assignment" warning. Go ahead, make it an "Error" <b>if you dare</b>.<br />
<br />
Marking an object's members as final is much more useful. I've always though of object members as mini-global variables. Global variables are bad but global constants are less so. When followed religiously, it allows the maintainer to see at a glance which references are being mutated or ideally that non are. When combined with extensive use of immutable objects it also allows you to quickly see what is being mutated if anything.<br />
<br />
While I don't bother marking method parameters final, I try and mark every class and object member I can as final. I've gone so far as to re-write code to mark more members final. I find it helps me understand my code and code of others faster than if I didn't and avoid errors too.<br />
<br />Andrewhttp://www.blogger.com/profile/17391659235860245424noreply@blogger.com0