An Ad-Hoc Analysis and Fix - in workspace
Object Example

Our system has people (of class Person), who have BinSupplies, which have Bins, which have Parts. Parts can have TransactionParts. When TransactionParts come into the system, each one creates a Part, which is tied back to the TransactionPart. The Part is put in a Bin.

We discovered a Bin with two parts where there should have been one. This indicated that something was wrong. Soon we discovered that something had been done twice that should have been done once, and that there were an unknown number of people in the system who could have the problem. We needed to fix the database. We quickly found how to identify people who could have the problem, and now had to determine which Bins, if any, were affected, and we had to fix them.

We determined that the extra parts would have the identical TransactionPart as the original good one. Our mission (and we decided to accept it) was to identify all Bins exhibiting the problem.

We inspected our way into the Bin that we knew had the problem. Now we wanted to write some code that would show the problem. We decided that since the TransactionParts were identical, we would make an IdentityDictionary by TransactionPart, holding a collection of the parts that had that TransactionPart. If things were bad, some collection would have more than one element. Along the way we realized that we had to consider the Part amount as well, so we added a layer of Dictionary by amount.

We wrote this patch of code to classify the bin:

transDict := Collection identityDictionary.
bin partsDo: 
	[ :eachPart | | dateDict amountDict collection |
	dateDict := transDict 
		at: eachPart transactionPart
		ifAbsentPut: [Collection dictionary].
	amountDict := dateDict
		at: eachPart effectiveOn
		ifAbsentPut: [Collection dictionary].
	collection := amountDict 
		at: eachPart amount
		ifAbsentPut: [Collection orderedCollection].
	collection add: eachPart].

Followed by this patch to see who had too many parts:

transDict values do: 
	[ :eachDateDict |
	eachDateDict values do: 
		[ :eachAmountDict |
		eachAmountDict values do: 
			[ :eachCollection |
			eachCollection size > 2 ifTrue:
				[eachCollection do: 
					[ :eachPart |
					Transcript
						show: eachPart printString;
						cr].
				Transcript
					show: '----';
					cr]]]

Now this is already pretty complicated. It’s not easy to see how the Dictionaries nest, but we were on top of the situation and knew what we were doing (at the time).

OK! Now all we have to do is build this code into a loop over all bins, and build that into a loop over all the identified people. The result (stand back) looks like this:

"to be done in an inspector on the identified Persons"

self do: 
	[ :eachPerson | | binSupply printed |
	printed := false.
	binSupply := eachPerson binSupply.
	binSupply corporations do: 
		[ :eachCorp | 
		#(HSEntRawInput) do: 
			[ :eachBinName | | bin transDict dateDict amountDict |
			bin := binSupply
				bin: eachBinName
				corporation: eachCorp.
			transDict := Collection identityDictionary.
			bin partsDo: 
				[ :eachPart | | collection |
				dateDict := transDict 
					at: eachPart transactionPart
					ifAbsentPut: [Collection dictionary].
				amountDict := dateDict
					at: eachPart effectiveOn
					ifAbsentPut: [Collection dictionary].
				collection := amountDict 
					at: eachPart amount
					ifAbsentPut: [Collection orderedCollection].
				collection add: eachPart].
			transDict values do: [ :eachDateDict |
				eachDateDict values do: [ :eachAmountDict |
					eachAmountDict values do: [ :eachCollection |
						eachCollection size > 2 ifTrue:
							[eachCollection do: 
							[ :eachPart | printed ifFalse:
								[printed := true.
								Transcript 
									cr; 
									show: eachPerson messageHeader; 
									cr].
								Transcript 
									show: eachPart printString; 
									cr]. 
							Transcript 
								show: '-----'; 
								cr]]]]]]]

We took the above code and saved it as a class method on Person, so we could find it if at some future time we needed to do it all again. We went home for the night.

Note: I think the above is correctly formatted, but to be honest, I'm not sure, between HTML and the complexity of the code itself. In a way, that's the point!

Let's look at a better solution.


© 1997, 1998, Ronald E Jeffries
ronjeffries@acm.org
http://www.xprogramming.com