|
Look Around You posted:Well, why not pull the method out of the switch statement so that you're only using it at one point? Also, if you print a generic error on 99 and return it, when will your program halt? Is there another specific code to terminate? Was going to put the switch in a while statement and end at -1, as it is in my original program. If I only use it once, wouldn't I then have to put all the output associated with each case, as well as the incrementer that will keep track of how many time they are used, inside this new method?
|
# ? Feb 22, 2012 06:57 |
|
|
# ? May 16, 2024 17:31 |
|
Tots posted:Was going to put the switch in a while statement and end at -1, as it is in my original program. Oh, it looks like the cases are significantly different. One way to break it apart (and probably the best way for larger applications) would be to take each case and turn it into it's own function. That way you can just do case 1: calcManagerSalary(); break; case 2: calcBlahWages(); break; and move the actual logic for calculating the wages into their own functions. This has the side effect of being a lot easier to debug too. But yes, you would have to figure out a place to store the incrementers and increment it when you calculate that specific type of wage.
|
# ? Feb 22, 2012 07:07 |
|
Plorkyeran posted:I've actually seen co-op hirability used as an argument for teaching Scheme in the intro classes - the logic being that they'd had 100% placement for a few years, so the school should be focusing on getting the students good jobs rather than any job at all, and companies that like to see Scheme on a resume are probably better places to work. At my school the beginning programming class was DrScheme. I think it was more to weed out than anything else really.
|
# ? Feb 22, 2012 07:13 |
|
Look Around You posted:Oh, it looks like the cases are significantly different. One way to break it apart (and probably the best way for larger applications) would be to take each case and turn it into it's own function. That way you can just do case 1: calcManagerSalary(); break; case 2: calcBlahWages(); break; and move the actual logic for calculating the wages into their own functions. This has the side effect of being a lot easier to debug too. But yes, you would have to figure out a place to store the incrementers and increment it when you calculate that specific type of wage. I am really tired right now, but I can't make sense of how this would work unless a method could return a string E: Okay, yah I need to go to bed. They wouldn't need to return anything. Good night.
|
# ? Feb 22, 2012 07:15 |
|
Tots posted:I am really tired right now, but I can't make sense of how this would work unless a method could return a string I know you went to bed and figured it out (you're right, they don't need to return anything), methods can in fact return String objects... in fact, they can basically return any type you want. For example: code:
Look Around You fucked around with this message at 07:45 on Feb 22, 2012 |
# ? Feb 22, 2012 07:41 |
|
Internet Janitor posted:pokeyman: I was mainly reacting to the assertion that Java syntax is onerously arcane and confusing. What would you consider a good language for beginning programmers? I have no idea. Scheme seems like a decent fit, but I get the impression that it needs to be very well taught or you'll lose your students in the weeds. I'm surprised I've never heard of an intro programming course doing stuff in the browser, maybe using something like Processing. It (anything in the browser) is shareable, it's the best REPL you can get, it can do graphics without dickery, and it should score high marks in co-op interviews. Or try Ruby, which you can sometimes make it read like English so the syntax isn't so bad, using something like Hackety Hack or Shoes. The first language I ever started learning was PHP, because I wanted to "make the <form> work", and despite the well-deserved scorn it gets you can do a lot with it and it's everywhere. I guess my answer is: if I was teaching a programming class in two weeks, I'd base it entirely on Processing.js. If I was teaching it tomorrow, we'd follow Learn Python the Hard Way and I'd be the question answerer guy.
|
# ? Feb 22, 2012 07:46 |
|
Tots posted:If I wanted to abstract the input and error handling, would this be the right track? Others have given you great advice about your idea. But, I wanted to single this statement out and commend you for saying it. This tells me that you are developing a true understanding of how to use software as a tool to solve problems. You started out with the same giant mess of code that everyone creates when they first start learning to program. This is to be expected though; because you're learning, you are focusing on solving a dozen or so problems in relative independence of each other, then grouping them together to form a solution to a larger problem. The important thing is that you evaluated your solution, noticed emerging patterns and refactored your code around those patterns to make it more simple and robust. I'd venture to say that, out of your class of 20 or so people, you're one of maybe four people to both notice those patterns and try to generalize your solutions around them. So, good job. You still have a lot to learn, but if you continue to put this kind of effort into your education, then you will certainly succeed.
|
# ? Feb 22, 2012 08:27 |
|
At the risk of becoming unpopular, if I was teaching 6-12 year olds programming, I'd start with Flash (AS1/AS2, not AS3), just because there's a little bit of fluff before you get to start building something fun to play around with. It's an animation tool and it's universally hated, but it gives you good primitives and it's very easy to get something graphical up and running without effort, and then make it interactive. For those unaware of how Flash works, you have a timeline of frames, since it started as an animation tool. The playhead advances a customizable frames per second, and you can start/stop the playhead and jump to any frame number in the scene. If the playhead is moving, it will loop back to the start of the scene when advancing past the end of a scene. You can also put actions on a frame, to be executed when the playhead reaches that frame. The script context is global, so variables defined in one frame are global across all frames. With this you can already start to build interesting systems: If you want something to happen every second, you build a timeline that's FPS frames long, and put the action on the start of the timeline or the end of the timeline depending on when you want the action to happen. By using a variable, you can make it loop a specified number of times. Frame 1 has the init: count = 5;, and Frame FPS+1 has the loop: count --; if (count > 0) gotoAndPlay(2); else stop();. This started out in the Flash development community at first, until everyone settled on what I call the "3-frame game loop" pattern. Frame 1 is an init, Frame 2 is the Game Logic, and Frame 3 is gotoAndPlay(2);, which keeps Frame 2 executing every frame. Of course, this is lost on everybody now that we have AS3, but since it's so simple and easy to build interesting patterns. Once you get to a high enough level, you can also look at it as a very simplified machine: the playhead is the instruction pointer, frame numbers are the code addresses, and gotoAndPlay a jump instruction. With those basic primitives, we built do...while and while loops without even thinking of it. And if I was teaching the same set of students over time, I'd guide them into figuring out these concepts, and I'd let them now I'd tricked them into inventing these sorts of loops, and show them at a low level that, yes, loops are just basic patterns for jumps that so common that we have special cases for them in most programming languages. Language evolution is a neat thing.
|
# ? Feb 22, 2012 08:33 |
|
Suspicious Dish posted:At the risk of becoming unpopular, if I was teaching 6-12 year olds programming, I'd start with Flash (AS1/AS2, not AS3), just because there's a little bit of fluff before you get to start building something fun to play around with. It's an animation tool and it's universally hated, but it gives you good primitives and it's very easy to get something graphical up and running without effort, and then make it interactive. I taught myself to code in middle school using Flash (AS1 because that's all there was then) and I can confirm that it's actually pretty awesome for exactly this reason.
|
# ? Feb 22, 2012 09:13 |
|
This is probably more of a math question, but I feel like this is the best place to ask it. I'm looking to determine whether a given customer is a "seasonal" one, i.e. they tend to only make transactions during a certain time of year. I don't (initially) care what time of year it is, I more want to just have some kind of metric for example a number between 0 and 1 where 0 represents customers where all transactions are in the same month and 1 is customers with a more evenly-spread transaction history or something. The problem I'm having is coming up with a way to structure the data so that month 1 (Jan) and month 12 (Dec) are treated as "next" to each other. I don't care that a person who transacted in January 2011 and again in December 2011 had 10 months without a transaction in there, I care that the months were proximate to each other in a seasonal sense. I'm really not sure what to even google for here, any thoughts or help would be appreciated. Thanks.
|
# ? Feb 22, 2012 15:19 |
|
Sub Par posted:This is probably more of a math question, but I feel like this is the best place to ask it. I'm looking to determine whether a given customer is a "seasonal" one, i.e. they tend to only make transactions during a certain time of year. I don't (initially) care what time of year it is, I more want to just have some kind of metric for example a number between 0 and 1 where 0 represents customers where all transactions are in the same month and 1 is customers with a more evenly-spread transaction history or something. This isn't a trivial problem. You could have all sorts of distributions of purchases that would skew simplistic attempts to calculate this metric. Suppose you have a customer that purchases only in June (a birthday perhaps) and December (holidays). Should they be seasonal or not? One simplistic approach would be to bucket sort the months into seasonal purchases and then calculate the maximum difference between seasons. The higher the difference, the more seasonal they are. If you already have a method of determining the seasonality of a customer and your only problem is how to wrap the months, post some code. code:
|
# ? Feb 22, 2012 15:46 |
|
I don't have any code just yet, and maybe seasonal was the wrong word to use because I don't want to hard code months into seasons and then determine if their transactions all fall within those seasons. I am looking to be able to say "this person makes all their transactions in the same month" which is easy but then contrast that with "this other customer transacts in different months but they tend to be closely clumped i.e. mostly in June/July or Dec/Jan/Feb" and then "this customer seems to transact randomly or across many different months i.e. Jan/Mar/Aug/Nov". This would be irrespective of any traditional definition of what constitutes a "season" - I want to see if certain customers cluster their transactions thus forming their own "season".
|
# ? Feb 22, 2012 15:52 |
|
code:
|
# ? Feb 22, 2012 16:08 |
|
That's a very good idea, like a rolling X month window. Thanks a bunch, I can go from here with that.
|
# ? Feb 22, 2012 16:22 |
|
Suspicious Dish posted:At the risk of becoming unpopular, if I was teaching 6-12 year olds programming, I'd start with Flash (AS1/AS2, not AS3), just because there's a little bit of fluff before you get to start building something fun to play around with. It's an animation tool and it's universally hated, but it gives you good primitives and it's very easy to get something graphical up and running without effort, and then make it interactive. There's no shortage of other, better languages that let you quickly get something shiny and fun to play with going, though. When I was 6 I was using LOGO, whose entire reason for existence is basically this (and it turns out it's a Lisp dialect). My high school used Turing, which is good for this (as long as you avoid the networking library). Lua and Python aren't designed around this capability but have libraries that add it. Notably, none of these languages will send your students spiraling into the depths of madness when they write a += 1 by evaluating a twice.
|
# ? Feb 22, 2012 16:54 |
|
There's a thread over here about how to teach intro programming classes that might be worth reading for those of you who are interested in the topic.Sub Par posted:I don't have any code just yet, and maybe seasonal was the wrong word to use because I don't want to hard code months into seasons and then determine if their transactions all fall within those seasons. I am looking to be able to say "this person makes all their transactions in the same month" which is easy but then contrast that with "this other customer transacts in different months but they tend to be closely clumped i.e. mostly in June/July or Dec/Jan/Feb" and then "this customer seems to transact randomly or across many different months i.e. Jan/Mar/Aug/Nov". For each customer, compute the proportion of their purchases that fall in each month, and compute the KL divergence between that and a uniform distribution. The lower the result, the less "seasonal" they are. ultrafilter fucked around with this message at 17:41 on Feb 22, 2012 |
# ? Feb 22, 2012 17:03 |
|
I just found something neat and relevant here (pdf : "The use of a Kolmogorov-Smirnov type statistic in testing hypotheses about seasonal variation", L.S. Freedman 1979, Journal of Epidemiology and Community Health). I'll try to summarize it, but beware that I'm very definitely not a statistician. First, calculate what fraction of sales for a customer happened each month, then, calculate the cumulative sums for that list (That is, make a new list where the value for a month is the sum of that month plus all earlier months). December ought to get 1. Now, make another list of how large a fraction of sales you'd expect to have happened if sales were perfectly even (and for the sake of simplicity, the months equally long) : 1/12 at the end of jan, 2/12 at the end of feb, up to 12/12 at the end of dec. Subtract one list from the other, so difference = actual-expected. If the data had not been seasonal, you could then find the largest absolute value in that list of differences, and use that as a score: Larger means more different. However, since these are months and wrap around, a small bit of compensation is needed. Find the largest number in the list of differences, and call it Dmax. Find the smallest (quite possibly negative) number, take the absolute of it, and call it Dmin. The interesting score is Dmin+Dmax - which stays constant if you rotate the months around. To illustrate, consider these distributions: code:
The blue line is the cumulative sales fraction for the theoretical "every month is identical"-case, the red line is the actual cumulative sales, and the green dots the difference each month. The score in the simple non-seasonal case is the maximum of the green dots. Dmin is the green value where the red is the furthest below (or least above) the blue, and Dmax is the green value where the red is the furthest above the blue. As for the scores: code:
edit: Aha, there's a simple enough lookup table (calculated from 10000 random tests, which I guess was a bit of work in 1979). Take the score, multiply it with sqrt(12), and look up here: code:
code:
Computer viking fucked around with this message at 20:45 on Feb 22, 2012 |
# ? Feb 22, 2012 20:02 |
|
Otto Skorzeny posted:I don't think anything is, although there are varying degrees of unreadability and bewilderment. I think that python would make a reasonably good first programming language for non-CS majors, and a poor one for CS and related majors. This is the exact position my school's CS program takes. They teach Python for the non-CS major intro classes (math majors (and maybe some other majors too?) have to take an intro CS class and they can take the non-major intro) and Ada for the CS majors' intro. The professors I've talked to give a few reasons for this, but the biggest one is that Python hides a lot of concepts that are relatively important to CS majors but are pretty unimportant for people just dabbling (for example, the concept of an independent compiler can be completely lost if you only use Python). Of course, their decision sort of leads into the question of "why Ada for CS majors" but I think that can and should be a different topic altogether. In short, you should pick a first language to teach based on the end goal, just like one often chooses a language to use for a project based on the end goal.
|
# ? Feb 22, 2012 20:49 |
|
I'm not entirely sure if I agree with that line of thought. If we make the (flawed, but I suspect less so each year) assumption that CS students and non-CS students start off with the same understanding (or lack of such), why should their introductions be different? If we also assume (I know...) that the introduction a non-CS student gets is a subset of what a CS student is supposed to learn the first year ... wouldn't the best language for that subset be the same for the two? In other words. Teach the basic concepts (splitting problems into implementable chunks, statements/functions/variables/flow, debugging, maybe a small bit of OO) in whatever language makes that the simplest, then move the CS students over to another language in their next, more advanced, course. Besides, the training in jumping between languages is useful in itself. Computer viking fucked around with this message at 21:01 on Feb 22, 2012 |
# ? Feb 22, 2012 20:57 |
|
Computer viking posted:I'm not entirely sure if I agree with that line of thought. If we make the (flawed, but I suspect less so each year) assumption that CS students and non-CS students start off with the same understanding (or lack of such), why should their introductions be different? If we also assume (I know...) that the introduction a non-CS student gets is a subset of what a CS student is supposed to learn the first year ... wouldn't the best language for that subset be the same for the two? I really do think that's a pretty valid way to think about it too. The point at which I differ, however, is that there are some things that are necessary to spend time talking about when you teach, for example, Ada as a first language as opposed to Python (to use the example of my program). Those things, while almost always very important if you are going to be spending 3+ years studying CS, probably don't matter to people that are going to be spending the rest of their lives studying other things or working in unrelated fields. Teaching Python allows these latter people to be exposed to the very basics of CS and gives them an idea of what kinds of concepts are important to the field without forcing them to get too specific. CS majors, on the other hand, get exposed to these same things but also get a head start into the specifics and more advanced topics. Also, this discussion began in regards to Java's relatively more difficult syntax being a barrier of entry. I do think that also can come into play in this discussion. I don't think many (any) people would argue that Python's syntax is more difficult than Java or Ada. You can make things happen with a Python program (or even with the REPL) with much less of a time investment. The problem is that the simpler syntax generally means you're being sheltered from some concepts that may or may not be incredibly important.
|
# ? Feb 22, 2012 21:11 |
|
etcetera08 posted:I really do think that's a pretty valid way to think about it too. The point at which I differ, however, is that there are some things that are necessary to spend time talking about when you teach, for example, Ada as a first language as opposed to Python (to use the example of my program). Those things, while almost always very important if you are going to be spending 3+ years studying CS, probably don't matter to people that are going to be spending the rest of their lives studying other things or working in unrelated fields. Teaching Python allows these latter people to be exposed to the very basics of CS and gives them an idea of what kinds of concepts are important to the field without forcing them to get too specific. CS majors, on the other hand, get exposed to these same things but also get a head start into the specifics and more advanced topics. The question is then if it's harmful or time-wasting in the long term to start out with a language where you can ignore those things (only to bring them up later), compared to plowing straight into them from the beginning. Personally, I don't think it's a bad idea to start out by easing them into programming as an activity first, and moving into the complications and underpinnings later. It's easier to add more details later than to absorb everything at once, in my experience - even if the "details" are large and fundamental. I don't teach programming or CS, though; maybe it would lead to annoying habits and weird misunderstandings that had to be polished out later.
|
# ? Feb 22, 2012 21:18 |
|
Look Around You posted:
I'm just going to comment on this, because this is bad practice in the Python community. One of my biggest dislikes of Python is that yes, you can do something like this, but it's a terrible idea. This is what it should be: code:
(Rant: this is, of course, a terrible design decision that just screwed me over the other day in a large system - I put a main block in one of my modules so I could easily test it by running it, and I was wondering my system was "desynced", so to speak)
|
# ? Feb 22, 2012 22:04 |
|
It's only a terrible idea if there's a remote chance that you'll use the file as a module, though. (In other words, skipping it is fine if you know that you are doing it, and why). Admittedly, my day-to-day python tends to be ugly little once-off scripts.
|
# ? Feb 22, 2012 22:18 |
|
Suspicious Dish posted:I'm just going to comment on this, because this is bad practice in the Python community. One of my biggest dislikes of Python is that yes, you can do something like this, but it's a terrible idea. This is what it should be: Yeah, I knew there was something like that in Python... I haven't really used python all that much but I figured there was something like that in it at least. The thing I'm saying though is that in (the first part of) an intro course you probably should be focusing more on things like loops, functions, stuff like that. For that stuff, single file scripts are probably the right way to go, and so you don't need to teach them that. I'd introduce the if __name__ == __main__: stuff when you introduce modules, because it's not necessary to know when you're literally just writing single file scripts. e: what I'm saying is basically don't include more "magic words" than you need to for introducing basic concepts. If you can write a single file script without that (which afaik you can), then you don't need to tell them to use it until you get to the point where you are actually going to be using multiple files/modules. Look Around You fucked around with this message at 23:48 on Feb 22, 2012 |
# ? Feb 22, 2012 23:46 |
|
Since my questions sparked the intro debate I should mention that I am in IST which is slightly different than CS. The introduction course for CS uses C++ as does the introduction course for students that aren't going into technology related major. Before taking this course which uses Java I had to take the generic intro which uses C++. That course just taught concepts like using variables and basic loops.
|
# ? Feb 23, 2012 00:21 |
|
pokeyman posted:I'm surprised I've never heard of an intro programming course doing stuff in the browser, maybe using something like Processing. It (anything in the browser) is shareable, it's the best REPL you can get, it can do graphics without dickery, and it should score high marks in co-op interviews. It's not a real programming course but there was this: http://cdsmith.wordpress.com/2011/08/16/haskell-for-kids-week-1/
|
# ? Feb 23, 2012 22:41 |
|
A dumb regular expression question because I'm slow and have never really looked at these things before. It's probably staring me in the face but my investigations were dead ends. Possibly a lack of understanding of the terminology. I have S[0-9]{4,5} Which I think says "S" followed by four or five numeric digits. If I want to match a string that contains that and only that I do this: ^S[0-9]{4,5}$ What I can't figure out is how I allow for various iterations of that within the expression I'm matching. I know I can repeat the whole thing inside the ^$ for a specific number. But what about if it needs to be one or more... or less than 5 or something like that? VVV perfect. Thanks. Gravy Jones fucked around with this message at 00:12 on Feb 24, 2012 |
# ? Feb 24, 2012 00:00 |
|
Gravy Jones posted:What I can't figure out is how I allow for various iterations of that within the expression I'm matching. I know I can repeat the whole thing inside the ^$ for a specific number. But what about if it needs to be one or more... or less than 5 or something like that? Use parens to group. One or more: ^(S[0-9]{4,5})+$ Less then five: ^(S[0-9]{4,5}){0,4}$
|
# ? Feb 24, 2012 00:09 |
|
Gravy Jones posted:A dumb regular expression question because I'm slow and have never really looked at these things before. It's probably staring me in the face but my investigations were dead ends. Possibly a lack of understanding of the terminology. one or more: ^S\d+$ less than 5: ^S\d{0,4}$ (or ^S\d{1,4}$ if it needs one or more) \d is a shortcut for [0-9]. e: I'm dumb, yeah put it in parens for grouping. ^(S\d{4,5})+$ ^(S\d{4,5}){0,5}$ e2 I was paren happy, too many close parens. Look Around You fucked around with this message at 00:14 on Feb 24, 2012 |
# ? Feb 24, 2012 00:11 |
|
E: Never mind.
Safe and Secure! fucked around with this message at 05:00 on Feb 24, 2012 |
# ? Feb 24, 2012 04:56 |
|
Can anybody help me with this sed command, I'm trying to remove carriage returns from some files. The command worked for most of hte files, but this one is being a bitch for some reason. code:
code:
|
# ? Feb 24, 2012 21:59 |
|
oRenj9 posted:Can anybody help me with this sed command, I'm trying to remove carriage returns from some files. The command worked for most of hte files, but this one is being a bitch for some reason. I don't know what could be causing this*, but is there a compelling reason why you can't just run dos2unix on the files? * Ok, if you were doing this on windows (say, using cygwin with the "open text files as text" option) sed would be seeing \r\n as \n alone and not doing any replacement), but you said it worked on other files, so it can't be that.
|
# ? Feb 24, 2012 22:43 |
|
I'm on OSX, so there is no dos2unix. However, I might have been mistaken on it working for other files. Looking through my command history, I found some `tr -d '\015'` in there which might account for why I thought it worked on some files. I think I'm just going to give up on sed and use tr anyway.
|
# ? Feb 24, 2012 23:22 |
|
I have an OOP design question. I have a class that answers Permission questions. The API is nice and simple to use:php:<? $perms = new Permissions(); if($perms->mayUserOpenDoor($user, $door)) { $door->open(); }?> php:<? class Permissions { function __construct() { $this->db_connection = ServiceFinder::getDatabaseConnection(); } }?> php:<? class Permissions { function __construct($db_connection) { $this->db_connection = $db_connection; } }?> php:<? class PermissionsFacade { function __construct() { $db_connection = ServiceFinder::getDatabaseConnection(); $this->permissions_calculator = new Permissions($db_connection); } function mayUserOpenDoor($user, $door) { return $this->permissions_calculator->mayUserOpenDoor($user, $door); } }?> - The "application" client instantiates the PermissionsFacade, and is thereby insulated from knowing what the (internal) Permissions class needs. - The "testing" client instantiates Permissions and since Permissions declares all its dependencies up-front, the testing client can inject mock/test-double database connections for speedy unit tests. Does this seem right? It smells wrong to me because PermissionsFacade itself can't be easily tested, as it's not declaring all its dependencies up front. What's the best way to go about this?
|
# ? Feb 25, 2012 00:28 |
|
minato posted:I have an OOP design question. I have a class that answers Permission questions. The API is nice and simple to use: The problem with this API is that it's entirely possible for the programmer not to write the check. So instead how about : php:<? $perms = new Permissions(); $door->open($perms, $user); ?> php:<? $userperms = new PermissionsForUser($user); $door->open($userperms); ?> Also, having it have its own database connection is icky. Is there not already an existing database connection? With an independent db connection, your permission-checking ends up not being rolled up in the same transaction as your other operations, if they modify the database. If this point is valid, you'll have to pass the database connection to the Permissions object, or maybe there's no need for a persistent permissions object, I don't know what other state it has. If there is no other database connection or the db is read-only, I would still pass the permissions object separately. Random pieces of code in your project should not just secretly be asking for permissions, opening database connections behind your back. shrughes fucked around with this message at 00:38 on Feb 25, 2012 |
# ? Feb 25, 2012 00:34 |
|
I don't disagree with anything you said, but you focused on areas that weren't the core problem I'm trying to address. I want to resolve the conflict between wanting to hide dependencies (like the DB connection) from the "application" client, but not from the "testing" client.
|
# ? Feb 25, 2012 00:51 |
minato posted:I don't disagree with anything you said, but you focused on areas that weren't the core problem I'm trying to address. I want to resolve the conflict between wanting to hide dependencies (like the DB connection) from the "application" client, but not from the "testing" client. What is to prevent you from replacing the ServiceFinder in this? minato posted:
Of course you want to find different services when you are in a testing environment. It's natural to replace the ServiceFinder.
|
|
# ? Feb 25, 2012 00:59 |
|
Replacing ServiceFinder occurred to me too, but apparently a "ServiceFinder" is a testability anti-pattern because it's effectively using global state, and doesn't make it clear to anyone using the class that the ServiceFinder is being used at all. IOW if I was to employ a unit test writer to test that class, they'd have to reverse-engineer the code to understand the dependencies instead of relying solely on the API (i.e. white-box testing vs black-box testing).
|
# ? Feb 25, 2012 01:12 |
minato posted:Replacing ServiceFinder occurred to me too, but apparently a "ServiceFinder" is a testability anti-pattern because it's effectively using global state, and doesn't make it clear to anyone using the class that the ServiceFinder is being used at all. IOW if I was to employ a unit test writer to test that class, they'd have to reverse-engineer the code to understand the dependencies instead of relying solely on the API (i.e. white-box testing vs black-box testing). Then do what shrughes suggests. Don't have a "permissions master" that exists outside of users or whatever kind of role-system you have. Your problem, as you have identified, is that your permissions master needs to access the database or otherwise, and it needs to know what database to access for that. You need to design your system so either you will be passing some global database object around, or the database is only explicitly referenced in a few locations. What I would offhand suggest was having your User class know about permissions and implement an interface to check for a user having some specified permission. Then, for every operation, you pass in a user object that specifies the user attempting to perform the operation, and the operation implementation will query for the permission needed. You can then test your operations by supplying a mock user object. Your real user class would be the one to know about the database, creating/obtaining a user object would involve the database. When you then need to test the user class you can supply a mock database. (Or supply a real database, but a connection to a testing environment populated with mock data.) Your current design requires you to pass around a global database object, either as an explicit global or as a "fake" global. (In theory, a global is like a value passed by reference to every single function everywhere. The difference is just whether you make it explicit that it's going in.) Change your design or live with it.
|
|
# ? Feb 25, 2012 01:41 |
|
|
# ? May 16, 2024 17:31 |
|
Thanks for the input, I appreciate it. What you suggest is unfortunately not possible for this app (politically, if not technically). After some reading and discussion, the issue I had with ServiceFinder being effectively global may not be such an issue after all. The nuance that was eluding me was that it's not important to make every single class in the app testable via dependency injections, just the classes that do meaningful work. In other words, the glue code isn't worth unit testing. So I might end up writing this: php:<? class MyApplication { $perms = $PermissionsFactory->getPermissions(); if($perms->mayUserOpenDoor($user, $door)) { ... } } // My job is to do object-graph creation class PermissionsFactory { function getPermissions() { $db = ServiceFinder::getDatabaseConnection(); return new Permissions($db); } } // My job is to answer permissions questions class Permissions { function __construct($db) { $this->db = $db; } function mayUserOpenDoor($user, $door) { return $this->db->someDatabaseSql(); } }?>
|
# ? Feb 25, 2012 03:56 |